2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

14_SD_WS_Maverick.pm: reworked maverick module

git-svn-id: https://svn.fhem.de/fhem/trunk@16508 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
sidey79 2018-03-29 19:39:14 +00:00
parent cc3d05e485
commit a09bf2c735
2 changed files with 266 additions and 115 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- change: 14_SD_WS_Maverick: reworked module, renamed readings
added commandref
- bugfix: 14_SD_WS07: Error retrieving rssi value fixed
- feature: 14_SD_WS07: added correction-temp and correction-hum
added two models temp/temp-hum

View File

@ -3,7 +3,13 @@
#
# The purpose of this module is to support Maverick sensors
# Sidey79 & Cruizer 2016
# Ralf9 2018
#
# CHANGED
##############################################################################
# Version 1.1
# - changed: 14_SD_WS_Maverick: rename Readings for Temperatures
# - feature: 14_SD_WS_Maverick: added Readings for Sensor-states
package main;
@ -12,19 +18,26 @@ use strict;
use warnings;
#use Data::Dumper;
sub SD_WS_Maverick_Initialize($);
sub SD_WS_Maverick_Define($$);
sub SD_WS_Maverick_Undef($$);
sub SD_WS_Maverick_Parse($$);
sub SD_WS_Maverick_Attr(@);
sub SD_WS_Maverick_SetSensor1Inaktiv($);
sub SD_WS_Maverick_SetSensor2Inaktiv($);
sub SD_WS_Maverick_updateReadings($);
sub
SD_WS_Maverick_Initialize($)
{
my ($hash) = @_;
$hash->{Match} = "^P47#AA9995[A-Fa-f0-9]+";
$hash->{Match} = "^P47#[A-Fa-f0-9]+";
$hash->{DefFn} = "SD_WS_Maverick_Define";
$hash->{UndefFn} = "SD_WS_Maverick_Undef";
$hash->{ParseFn} = "SD_WS_Maverick_Parse";
$hash->{AttrFn} = "SD_WS_Maverick_Attr";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " .
$hash->{AttrFn} = "SD_WS_Maverick_Attr";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 inactivityinterval " .
"$readingFnAttributes ";
$hash->{AutoCreate} =
{ "SD_WS_Maverick.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", autocreateThreshold => "2:180"} };
@ -50,6 +63,35 @@ SD_WS_Maverick_Define($$)
$hash->{STATE} = "Defined";
my $name= $hash->{NAME};
# prüfen, ob eine neue Definition angelegt wird
if($init_done && !defined($hash->{OLDDEF}))
{
# setzen von stateFormat
$attr{$name}{"stateFormat"} = '{
my $s1=ReadingsVal($name,"Sensor-1-food_state",-1);
my $s2=ReadingsVal($name,"Sensor-2-bbq_state",-1);
if ($s1 ne "connected" && $s1 eq $s2 ) {
return $s1;
}else{
my $state="Food: ";
my $temp_food=ReadingsVal($name,"temp-food","");
my $temp_bbq=ReadingsVal($name,"temp-bbq","");
if($s1 eq "connected"){
$state .=$temp_food;
}else{
$state .=$s1;
}
$state .=" BBQ: ";
if($s2 eq "connected"){
$state .=$temp_bbq;
}else{
$state .=$s2;
}
return $state;
}
}';
}
return undef;
}
@ -80,112 +122,211 @@ SD_WS_Maverick_Parse($$)
#my $blen = $hlen * 4;
#my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
Log3 $name, 3, "SD_WS_Maverick_Parse $model ($msg) length: $hlen";
Log3 $name, 4, "$name SD_WS_Maverick_Parse $model ($msg) length: $hlen";
# https://hackaday.io/project/4690-reverse-engineering-the-maverick-et-732/
# https://forums.adafruit.com/viewtopic.php?f=8&t=25414&sid=e1775df908194d56692c6ad9650fdfb2&start=15#p322178
#
#1 8 13 18 26
#AA999559 55555 95999 A9A9A669 Sensor 1 =21 2Grad
#AA999559 95996 55555 95A65565 Sensor 2 =22 2Grad
#
#Header Sen1 Sens2
#my $hashumidity = FALSE;
#
## Todo: Change decoding per model into a foreach
#foreach $key (keys %models) {
#foreach $key (keys %models) {
# ....
#}
#
my $startup = substr($rawData,6,2);
my $temp_str1 = substr($rawData,8,5);
my $temp_str2 = substr($rawData,13,5);
my $unknown = substr($rawData,18);
Log3 $iohash, 4, "$model decoded protocolid: 47 sensor startup=$startup, temp1=$temp_str1, temp2=$temp_str2, unknown=$unknown";
# Convert
# ohne header:
# MC;LL=-507;LH=490;SL=-258;SH=239;D=AA9995599599A959996699A969;C=248;L=104;
# P47#599599A959996699A969
#
# 0 2 6 7 12
# ss 11111 22222 uuuuuuuu
# 59 9599A 95999 6699A969
#
my $messageType = substr($rawData,0,2); # 0x6A upon startup, 0x59 otherwise
my $temp_str1 = substr($rawData,2,5);
my $temp_str2 = substr($rawData,7,5);
my $checksum_str = substr($rawData,12);
Log3 $iohash, 4, "$name $model decoded protocolid: 47 sensor messageType=$messageType, temp-f=$temp_str1, temp-b=$temp_str2, checksum-s=$checksum_str";
if ($messageType eq '59'){
$messageType="normal";
}elsif ($messageType eq '6A') {
$messageType="sync";
}else{
Log3 $iohash, 4, "$name $model ERROR: wrong messageType=$messageType (must be 59 or 6A)";
return '';
}
# Calculate temp from data
my $c;
my $temp_food=-532;
my $temp_bbq=-532;
my $sensor_1_state="unknown";
my $sensor_2_state="unknown";;
if ($temp_str1 ne '55555') {
$temp_str1 =~ tr/569A/0123/;
$temp_str2 =~ tr/569A/0123/;
# Calculate temp from data
my $c;
my $temp1=-532;
for ( my $i = 0; $i < length($temp_str1); $i++ ) {
$c = substr( $temp_str1, $i, 1);
$temp1 += $c*4**(4-$i);
}
my $temp2=-532;
for ( my $i = 0; $i < length($temp_str2); $i++ ) {
$c = substr( $temp_str2, $i, 1);
$temp2 += $c*4**(4-$i);
}
Log3 $iohash, 4, "$model decoded protocolid: temp1=$temp1, temp2=$temp2;";
for ( my $i = 0; $i < length($temp_str1); $i++ ) {
$c = substr( $temp_str1, $i, 1);
$temp_food += $c*4**(4-$i);
}
if ($temp_food <= 0 || $temp_food > 300) {
Log3 $iohash, 4, "$name $model ERROR: wrong temp-food=$temp_food";
$temp_food = "";
}else{
$sensor_1_state="connected";
}
} else {
$sensor_1_state="disconnected" if ($temp_str1 eq '55555');
$temp_food = "";
}
#print Dumper($modules{SD_WS_Maverick}{defptr});
my $def = $modules{SD_WS_Maverick}{defptr}{$iohash->{NAME} };
$def = $modules{SD_WS_Maverick}{defptr}{$model} if(!$def);
if(!$def) {
Log3 $iohash, 1, 'SD_WS_Maverick: UNDEFINED sensor ' . $model;
return "UNDEFINED $model SD_WS_Maverick $model";
if ($temp_str2 ne '55555') {
$temp_str2 =~ tr/569A/0123/;
for ( my $i = 0; $i < length($temp_str2); $i++ ) {
$c = substr( $temp_str2, $i, 1);
$temp_bbq += $c*4**(4-$i);
}
#Log3 $iohash, 3, 'SD_WS_Maverick: ' . $def->{NAME} . ' ' . $id;
my $hash = $def;
$name = $hash->{NAME};
Log3 $name, 4, "SD_WS_Maverick: $name ($rawData)";
if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef)))
{
my $minsecs = AttrVal($iohash->{NAME},'minsecs',0);
if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) {
Log3 $hash, 4, "$model Dropped due to short time. minsecs=$minsecs";
return "";
}
}
$hash->{lastReceive} = time();
$hash->{lastMSG} = $rawData;
#$hash->{bitMSG} = $bitData2;
my $state = "T: $temp1"." T2: $temp2" ;
if ($temp_bbq <= 0 || $temp_bbq > 300) {
Log3 $iohash, 4, "$name $model ERROR: wrong temp-bbq=$temp_bbq";
$temp_bbq = "";
}else{
$sensor_2_state="connected";
}
} else {
$sensor_2_state="disconnected" if ($temp_str2 eq '55555');
$temp_bbq = "";
}
#if ($temp_bbq eq "" && $temp_food eq "") {
# return '';
#}
Log3 $iohash, 4, "$name $model decoded protocolid: temp-food=$temp_food, temp-bbq=$temp_bbq;";
#print Dumper($modules{SD_WS_Maverick}{defptr});
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $state);
readingsBulkUpdate($hash, "messageType", $startup);
readingsBulkUpdate($hash, "temp1", $temp1) if ($temp1 ne"");
readingsBulkUpdate($hash, "temp2", $temp2) if ($temp2 ne"");
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
my $def = $modules{SD_WS_Maverick}{defptr}{$iohash->{NAME} };
$def = $modules{SD_WS_Maverick}{defptr}{$model} if(!$def);
return $name;
if(!$def) {
Log3 $iohash, 1, "$name SD_WS_Maverick: UNDEFINED sensor $model";
return "UNDEFINED $model SD_WS_Maverick $model";
}
#Log3 $iohash, 3, 'SD_WS_Maverick: ' . $def->{NAME} . ' ' . $id;
my $hash = $def;
$name = $hash->{NAME};
Log3 $name, 4, "SD_WS_Maverick: $name ($rawData)";
if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef)))
{
my $minsecs = AttrVal($iohash->{NAME},'minsecs',0);
if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) {
Log3 $hash, 4, "$model Dropped due to short time. minsecs=$minsecs";
return "";
}
}
$hash->{lastReceive} = time();
$hash->{lastMSG} = $rawData;
#$hash->{bitMSG} = $bitData2;
# Den SensorState bei Inaktivität zurücksetzen lassen durch Timer
my $inactivityinterval=int(AttrVal($name,"inactivityinterval",360));
if ($sensor_1_state ne "unknown") {
$hash->{sensor_1_state}=$sensor_1_state;
RemoveInternalTimer($hash, 'SD_WS_Maverick_SetSensor1Inaktiv');
InternalTimer(time()+($inactivityinterval), 'SD_WS_Maverick_SetSensor1Inaktiv', $hash, 0);
}
if ( $sensor_2_state ne "unknown") {
$hash->{sensor_2_state}=$sensor_2_state;
RemoveInternalTimer($hash, 'SD_WS_Maverick_SetSensor2Inaktiv');
InternalTimer(time()+($inactivityinterval), 'SD_WS_Maverick_SetSensor2Inaktiv', $hash, 0);
}
# Checksum auswerten
$checksum_str =~ tr/569A/0123/;
my $checksum="";
$checksum=$checksum_str;
# TODO: Die eigentliche Checksum errechnen. Diese ändert sich bei jedem Temperaturwechsel
# TODO: Evtl. ist in den checksum-bits auch noch eine Info zur Batterie enthalten
# ggf. ist es möglich die checksum als ID zu verwenden und so mehrere Mavericks in fhem einbinden zu können.
$hash->{checksum}=$checksum;
$hash->{temp_food}=$temp_food if ($temp_food ne"");
$hash->{temp_bbq}=$temp_bbq if ($temp_bbq ne"");
$hash->{messageType}=$messageType;
# TODO: Logging kann entfernt werden, wenn checksum entschlüsselt ist. Wird zur Analyse verwendet.
Log3 $hash, 4, "$name statistic: checksum=$checksum, t1=$temp_str1, temp-food=$temp_food, t2_$temp_str2, temp-bbq=$temp_bbq;";
SD_WS_Maverick_updateReadings($hash);
return $name;
}
sub SD_WS_Maverick_Attr(@)
{
my @a = @_;
# Make possible to use the same code for different logical devices when they
# are received through different physical devices.
return if($a[0] ne "set" || $a[2] ne "IODev");
my $hash = $defs{$a[1]};
my $iohash = $defs{$a[3]};
my $cde = $hash->{CODE};
delete($modules{SD_WS_Maverick}{defptr}{$cde});
$modules{SD_WS_Maverick}{defptr}{$iohash->{NAME} . "." . $cde} = $hash;
my ($cmd,$name,$attr_name,$attr_value) = @_;
my $hash = $defs{$name};
if($cmd eq "set") {
if($attr_name eq "IODev") {
# Make possible to use the same code for different logical devices when they
# are received through different physical devices.
my $iohash = $defs{$attr_value};
my $cde = $hash->{CODE};
delete($modules{SD_WS_Maverick}{defptr}{$cde});
$modules{SD_WS_Maverick}{defptr}{$iohash->{NAME} . "." . $cde} = $hash;
}
elsif($attr_name eq "inactivityinterval") {
if (!looks_like_number($attr_value) || int($attr_value) < 60 || int($attr_value) > 3600) {
return "$name: Value \"$attr_value\" is not allowed.\n"
."inactivityinterval must be a number between 60 and 3600."
}
}
}
return undef;
}
sub SD_WS_Maverick_SetSensor1Inaktiv($){
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $hash, 5, "$name SD_WS_Maverick_SetSensor1Inaktiv";
$hash->{sensor_1_state}="inactiv";
SD_WS_Maverick_updateReadings($hash);
}
sub SD_WS_Maverick_SetSensor2Inaktiv($){
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $hash, 5, "$name SD_WS_Maverick_SetSensor2Inaktiv";
$hash->{sensor_2_state}="inactiv";
SD_WS_Maverick_updateReadings($hash);
}
sub SD_WS_Maverick_updateReadings($){
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $hash, 5, "$name SD_WS_Maverick_updateReadings";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "temp-food", $hash->{temp_food});
readingsBulkUpdate($hash, "temp-bbq", $hash->{temp_bbq});
readingsBulkUpdate($hash, "messageType ", $hash->{messageType});
readingsBulkUpdate($hash, "checksum", $hash->{checksum});
readingsBulkUpdate($hash, "Sensor-1-food_state", $hash->{sensor_1_state});
readingsBulkUpdate($hash, "Sensor-2-bbq_state", $hash->{sensor_2_state});
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
return undef;
}
1;
@ -196,43 +337,45 @@ sub SD_WS_Maverick_Attr(@)
=begin html
<a name="SD_WS_Maverick"></a>
<h3>Wether Sensors protocol #7</h3>
<h3>BBQ Sensors protocol #47</h3>
<ul>
The SD_WS_Maverick module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.<br>
<br>
<b>Known models:</b>
<ul>
<li>Eurochon EAS800z</li>
<li>Technoline WS6750/TX70DTH</li>
<li>Maverick 732/733</li>
</ul>
<br>
New received device are add in fhem with autocreate.
New received device will be added in fhem with autocreate (if autocreate is globally enabled).
<br><br>
<a name="SD_WS_Maverick_Define"></a>
<b>Define</b>
<ul>The received devices created automatically.<br>
The ID of the defice is the cannel or, if the longid attribute is specified, it is a combination of channel and some random generated bits at powering the sensor and the channel.<br>
If you want to use more sensors, than channels available, you can use the longid option to differentiate them.
Maverick generates a random ID each time turned on. So it is not possible to link the hardware with the fhem-device.
The consequence is, that only one Maverick can be defined in fhem.
</ul>
<br>
<a name="SD_WS_Maverick Events"></a>
<b>Generated readings:</b>
<br>Some devices may not support all readings, so they will not be presented<br>
<ul>
<li>State (T: H:)</li>
<li>temperature (&deg;C)</li>
<li>humidity: (The humidity (1-100 if available)</li>
<li>battery: (low or ok)</li>
<li>channel: (The Channelnumber (number if)</li>
<li>State (Food: BBQ: )</li>
<li>temp-food (&deg;C)</li>
<li>temp-bbq (&deg;C)</li>
<li>Sensor-1-food_state (connected, disconnected or inactiv)</li>
<li>Sensor-2-bbq_state (connected, disconnected or inactiv)</li>
<li>messageType (sync at startup or resync, otherwise normal)</li>
<li>checksum (experimental)</li>
</ul>
<br>
<b>Attributes</b>
<ul>
<li>inactivityinterval <seconds (60-3600)><br>
The Interval to set Sensor-1-food_state and/or Sensor-2-bbq_state to inactiv after defined minutes. This can help to detect empty batteries or the malfunction of a tempertature-sensor.<br>
<code>default: 360</code></li>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#ignore">ignore</a></li>
<li><a href="#model">model</a> ()</li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#ignore">ignore </a></li>
<li><a href="#showtime">showtime (see FHEMWEB)</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>
@ -240,7 +383,7 @@ sub SD_WS_Maverick_Attr(@)
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS_Maverick_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<b>Parse</b> <ul>N/A</ul><br>
</ul>
@ -249,39 +392,45 @@ sub SD_WS_Maverick_Attr(@)
=begin html_DE
<a name="SD_WS_Maverick"></a>
<h3>SD_WS_Maverick</h3>
<h3>BBQ Sensors protocol #47</h3>
<ul>
Das SD_WS_Maverick Module verarbeitet von einem IO Geraet (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.<br>
<br>
<b>Unterst&uumltzte Modelle:</b>
<ul>
<li>Maverick</li>
<li>Maverick 732/733</li>
</ul>
<br>
Neu empfangene Sensoren werden in FHEM per autocreate angelegt.
Neu empfangene Sensoren werden in FHEM per autocreate angelegt (sofern autocreate in global aktiv ist).
<br><br>
<a name="SD_WS_Maverick_Define"></a>
<b>Define</b>
<ul>Die empfangenen Sensoren werden automatisch angelegt.<br>
Die ID der angelegten Sensoren ist entweder der Kanal des Sensors, oder wenn das Attribut longid gesetzt ist, dann wird die ID aus dem Kanal und einer Reihe von Bits erzeugt, welche der Sensor beim Einschalten zuf&aumlllig vergibt.<br>
Da das Maverick bei jedem Start eine neue zufällige ID erzeugt kann das Ger&aumlt nicht mit dem fhem-device gekoppelt werden.
Das bedeutet, dass es nicht m&oumlglich ist in fhem zwei Mavericks parallel zu betreiben.
</ul>
<br>
<a name="SD_WS_Maverick Events"></a>
<b>Generierte Readings:</b>
<ul>
<li>State (T: H:)</li>
<li>temperature (&deg;C)</li>
<li>humidity: (Luftfeuchte (1-100)</li>
<li>battery: (low oder ok)</li>
<li>channel: (Der Sensor Kanal)</li>
<li>State (Food: BBQ: )</li>
<li>temp-food (&deg;C)</li>
<li>temp-bbq (&deg;C)</li>
<li>Sensor-1-food_state (connected, disconnected oder inactiv)</li>
<li>Sensor-2-bbq_state (connected, disconnected oder inactiv)</li>
<li>messageType (sync bei Start oder resync, sonst normal)</li>
<li>checksum (experimentell)</li>
</ul>
<br>
<b>Attribute</b>
<ul>
<li>inactivityinterval <Sekunden (60-3600)><br>
Das Interval nach dem Sensor-1-food_state und/oder Sensor-2-bbq_state auf inactiv gesetzt werden, wenn keine Signale mehr empfangen werden.
Hilfreich zum erkennen einer leeren Batterie oder eines defekten Termperaturf&uumlhlers.<br>
<code>default: 360</code></li>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#ignore">ignore</a></li>
<li><a href="#model">model</a> ()</li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>
@ -290,7 +439,7 @@ sub SD_WS_Maverick_Attr(@)
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS_Maverick_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<b>Parse</b> <ul>N/A</ul><br>
</ul>