############################################## # $Id$ # # The purpose of this module is to support serval eurochron # weather sensors like eas8007 which use the same protocol # Sidey79, Ralf9 2015-2017 # Sidey79, elektron-bbs 2018-2019 # # Nexus sensor protocol with ID, temperature and optional humidity # also FreeTec NC-7345 sensors for FreeTec Weatherstation NC-7344. # # the sensor sends 36 bits 12 times, # the packets are ppm modulated (distance coding) with a pulse of ~500 us # followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a # 1 bit, the sync gap is ~4000 us. # # the data is grouped in 9 nibbles # [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1] # # The 8-bit id changes when the battery is changed in the sensor. # flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW # and CC is the channel: 0=CH1, 1=CH2, 2=CH3 # temp is 12 bit signed scaled by 10 # const is always 1111 (0xF) or 1010 (0xA) # humiditiy is 8 bits package main; #use version 0.77; our $VERSION = version->declare('v3.4.3'); use strict; use warnings; use FHEM::Meta; #use Data::Dumper; sub SD_WS07_Initialize { my ($hash) = @_; $hash->{Match} = "^P7#[A-Fa-f0-9]{6}[AFaf][A-Fa-f0-9]{2,3}"; ## pos 7 ist aktuell immer 0xF oder 0xA $hash->{DefFn} = \&SD_WS07_Define; $hash->{UndefFn} = \&SD_WS07_Undef; $hash->{ParseFn} = \&SD_WS07_Parse; $hash->{AttrList} = "do_not_notify:1,0 ignore:0,1 showtime:1,0 " . "negation-batt:no,yes ". "max-deviation-temp:1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50 ". "offset-temp ". "$readingFnAttributes "; $hash->{AutoCreate} = { "SD_WS07_TH_.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, "SD_WS07_T_.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4:Temp,", autocreateThreshold => "2:180"} }; return FHEM::Meta::InitMod( __FILE__, $hash ) } ############################# sub SD_WS07_Define { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); return "wrong syntax: define SD_WS07 ".int(@a) if(int(@a) < 3 ); $hash->{CODE} = $a[2]; $hash->{lastMSG} = ""; $hash->{bitMSG} = ""; $modules{SD_WS07}{defptr}{$a[2]} = $hash; $hash->{STATE} = "Defined"; my $name= $hash->{NAME}; return undef; } ##################################### sub SD_WS07_Undef { my ($hash, $name) = @_; delete($modules{SD_WS07}{defptr}{$hash->{CODE}}) if(defined($hash->{CODE}) && defined($modules{SD_WS07}{defptr}{$hash->{CODE}})); return undef; } ################################### sub SD_WS07_Parse { my ($iohash, $msg) = @_; my (undef ,$rawData) = split("#",$msg); #$protocol=~ s/^P(\d+)/$1/; # extract protocol my $model = "SD_WS07"; my $typ = $model; my $hlen = length($rawData); my $blen = $hlen * 4; my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); Log3 $iohash, 4, "$iohash->{NAME}: SD_WS07_Parse $model ($msg) length: $hlen"; # 0 4 8 12 24 28 36 # 00110110 1010 000100000010 1111 00111000 0000 eas8007 # 01110010 1010 000010111100 1111 00000000 0000 other device from anfichtn # 11010010 0000 000000010001 1111 00101000 other device from elektron-bbs # 01100011 1000 000011101010 1111 00001010 other device from HomeAuto_User SD_WS07_TH_631 # 11101011 1000 000010111000 1111 00000000 other device from HomeAuto_User SD_WS07_T_EB1 # 11000100 1000 000100100010 1111 00000000 other device from HomeAuto_User SD_WS07_T_C41 # 01100100 0000 000100001110 1111 00101010 hama TS36E from HomeAuto_User - Bat bit identified # 01001101 1010 000011110101 1111 00001010 Mebus HQ7312-2 from rpsVerni https://github.com/RFD-FHEM/RFFHEM/issues/1024 # Long-ID BCCC TEMPERATURE ?? HUMIDITY B=Battery, C=Channel # 10110001 1000 000100011010 1010 00101100 Auriol AFW 2 A1, IAN: 297514 # Long-ID BSCC TEMPERATURE ?? HUMIDITY B=Battery, S=Sendmode, C=Channel # Modelliste my %models = ( "0" => "T", "1" => "TH", ); my $bitData2 = substr($bitData,0,8) . ' ' . substr($bitData,8,4) . ' ' . substr($bitData,12,12) . ' ' . substr($bitData,24,4) . ' ' . substr($bitData,28,8); Log3 $iohash, 4, "$iohash->{NAME}: SD_WS07_Parse $model converted to bits " . $bitData2; my $id = substr($rawData,0,2); my $bat = substr($bitData,8,1) eq "1" ? "ok" : "low"; # 1 = ok | 0 = low --> identified on hama TS36E my $sendmode; my $channel = oct("0b" . substr($bitData,9,3)) + 1; if (substr($bitData,24,4) eq "1010") { $sendmode = substr($bitData,9,1) eq "1" ? "manual" : "auto"; # 1 = manual | 0 = auto --> identified on Auriol AFW 2 A1 $channel = oct("0b" . substr($bitData,10,2)) + 1; } my $temp = oct("0b" . substr($bitData,12,12)); my $hum = oct("0b" . substr($bitData,28,8)); my $modelkey; if ($hum == 0) { $modelkey = $hum; } elsif ($hum != 0) { $modelkey = 1; } $model = $model."_".$models{$modelkey}; my $deviceCode; my $longids = AttrVal($iohash->{NAME},'longids',0); if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) { $deviceCode = $id.$channel; Log3 $iohash,4, "$iohash->{NAME}: using longid $longids model $model"; } else { $deviceCode = $channel; } ### Model specific attributes if ($models{$modelkey} eq "TH") { addToDevAttrList($model."_".$deviceCode,"max-deviation-hum:1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50 "); addToDevAttrList($model."_".$deviceCode,"offset-hum"); } #print Dumper($modules{SD_WS07}{defptr}); my $oldDeviceCode = $deviceCode; # temporary statement to find wrong definitions $deviceCode = $model . "_" . $deviceCode; my $def = $modules{SD_WS07}{defptr}{$deviceCode}; # test for already defined devices use normal naming convention (model_channel or model_lonid) if (!defined($def)) # temporary statement: fix wrong definition { $def = $modules{SD_WS07}{defptr}{$oldDeviceCode}; # test for already defined devices use wrong naming convention (only channel or longid) if(defined($def)) { Log3 $iohash,4, "$def->{NAME}: Updating decrepated DEF of this sensor. Save config is needed to avoid further messages like this."; CommandModify(undef,"$def->{NAME} $deviceCode") } } if(!$def) { Log3 $iohash, 1, "$iohash->{NAME}: UNDEFINED Sensor $model detected, code $deviceCode"; return "UNDEFINED $deviceCode SD_WS07 $deviceCode"; } #Log3 $iohash, 3, 'SD_WS07: ' . $def->{NAME} . ' ' . $id; my $hash = $def; my $name = $hash->{NAME}; return "" if(IsIgnored($name)); #Log3 $name, 4, "$iohash->{NAME} SD_WS07: $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, "$iohash->{NAME}: $deviceCode Dropped due to short time. minsecs=$minsecs"; return ""; } } $hum += AttrVal($name, "offset-hum", 0); # correction value for humidity (default 0 %) if ($model ne "SD_WS07_T" && $hum > 100 || $model ne "SD_WS07_T" && $hum < 0) { Log3 $name, 4, "$iohash->{NAME}: $name ERROR - Humidity out of range 0-100: ($hum)"; return ""; } if ($temp > 700 && $temp < 3840) { # -25,6 .. 70,0 °C Log3 $name, 4, "$iohash->{NAME}: $name ERROR - Temperature out of range 700-3840 ($temp)"; return ""; } elsif ($temp >= 3840) { # negative Temperaturen, ist ueberprueft worden $temp -= 4096; } $temp /= 10; $temp += AttrVal($name, "offset-temp", 0); # correction value for temperature (default 0 K) Log3 $iohash, 4, "$iohash->{NAME}: $name id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat"; # Sanity check temperature and humidity if($def) { my $timeSinceLastUpdate = ReadingsAge($hash->{NAME}, "state", 0); if ($timeSinceLastUpdate < 0) { $timeSinceLastUpdate = -$timeSinceLastUpdate; } if (ReadingsVal($name, "temperature", undef) && (defined(AttrVal($hash->{NAME},"max-deviation-temp",undef)))) { my $diffTemp = 0; my $oldTemp = ReadingsVal($name, "temperature", undef); my $maxdeviation = AttrVal($name, "max-deviation-temp", 1); # default 1 K if ($temp > $oldTemp) { $diffTemp = ($temp - $oldTemp); } else { $diffTemp = ($oldTemp - $temp); } $diffTemp = sprintf("%.1f", $diffTemp); Log3 $name, 4, "$iohash->{NAME}: $name old temp $oldTemp, age $timeSinceLastUpdate, new temp $temp, diff temp $diffTemp"; my $maxDiffTemp = $timeSinceLastUpdate / 60 + $maxdeviation; # maxdeviation + 1.0 Kelvin/Minute $maxDiffTemp = sprintf("%.1f", $maxDiffTemp + 0.05); # round 0.1 Log3 $name, 4, "$iohash->{NAME}: $name max difference temperature $maxDiffTemp K"; if ($diffTemp > $maxDiffTemp) { Log3 $name, 3, "$iohash->{NAME}: $name ERROR - Temp diff too large (old $oldTemp, new $temp, diff $diffTemp)"; return ""; } } if (defined($hash->{READINGS}{humidity}{VAL}) && defined(AttrVal($hash->{NAME},"max-deviation-hum",undef)) && $models{$modelkey} eq "TH") { my $diffHum = 0; my $oldHum = ReadingsVal($name, "humidity", undef); my $maxdeviation = AttrVal($name, "max-deviation-hum", 1); # default 1 % if ($hum > $oldHum) { $diffHum = ($hum - $oldHum); } else { $diffHum = ($oldHum - $hum); } Log3 $name, 4, "$iohash->{NAME}: $name old hum $oldHum, age $timeSinceLastUpdate, new hum $hum, diff hum $diffHum"; my $maxDiffHum = $timeSinceLastUpdate / 60 + $maxdeviation; # maxdeviation + 1.0 %/Minute $maxDiffHum = sprintf("%1.f", $maxDiffHum + 0.5); # round 1 Log3 $name, 4, "$iohash->{NAME}: $name max difference humidity $maxDiffHum %"; if ($diffHum > $maxDiffHum) { Log3 $name, 3, "$iohash->{NAME}: $name ERROR - Hum diff too large (old $oldHum, new $hum, diff $diffHum)"; return ""; } } } $hash->{lastReceive} = time(); $hash->{lastMSG} = $rawData; $hash->{bitMSG} = $bitData2; if (AttrVal($name, "negation-batt", "no") eq "yes") { # default undef negation batt bit $bat = "0" eq "0" ? "ok" : "low"; # 0 = ok } my $state = "T: $temp". ($hum>0 ? " H: $hum":""); readingsBeginUpdate($hash); #readingsBulkUpdate($hash, "model", $models{$modelkey}); readingsBulkUpdate($hash, "state", $state); readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne""); readingsBulkUpdate($hash, "humidity", $hum) if ($models{$modelkey} eq "TH"); readingsBulkUpdate($hash, "batteryState", $bat); readingsBulkUpdate($hash, "sendmode", $sendmode, 0) if (defined($sendmode)); readingsBulkUpdate($hash, "channel", $channel) if ($channel ne ""); readingsEndUpdate($hash, 1); # Notify is done by Dispatch ### ZusatzCheck | Beauty - humidity wird einmal definiert obwohl Typ T ### #delete $hash->{READINGS}{"humidity"} if($hash->{READINGS} && $models{$modelkey} eq "T"); delete $hash->{READINGS}{humidity} if($hash->{READINGS}{humidity} && $models{$modelkey} eq "T"); return $name; } 1; =pod =item summary Supports weather sensors protocol 7 from SIGNALduino =item summary_DE Unterstützt Wettersensoren mit Protokol 7 vom SIGNALduino =begin html

Weather Sensors protocol #7

=end html =begin html_DE

SD_WS07

=end html_DE =for :application/json;q=META.json 14_SD_WS07.pm { "abstract": "Supports weather sensors protocol 7 from SIGNALduino", "author": [ "Sidey <>", "ralf9 <>" ], "x_fhem_maintainer": [ "Sidey" ], "x_fhem_maintainer_github": [ "Sidey79", "HomeAutoUser", "elektron-bbs" ], "description": "The SD_WS07 module processes messages from various environmental sensors received from an IO device (CUL, CUN, SIGNALDuino, SignalESP etc.)", "dynamic_config": 1, "keywords": [ "fhem-sonstige-systeme", "fhem-hausautomations-systeme", "fhem-mod", "signalduino", "weather", "station", "sensor" ], "license": [ "GPL_2" ], "meta-spec": { "url": "https://metacpan.org/pod/CPAN::Meta::Spec", "version": 2 }, "name": "FHEM::SD_WS", "prereqs": { "runtime": { "requires": { } }, "develop": { "requires": { } } }, "release_status": "stable", "resources": { "bugtracker": { "web": "https://github.com/RFD-FHEM/RFFHEM/issues/" }, "x_testData": [ { "url": "https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/master/t/FHEM/14_SD_WS07/testData.json", "testname": "Testdata with SD_WS07 sensors" } ], "repository": { "x_master": { "type": "git", "url": "https://github.com/RFD-FHEM/RFFHEM.git", "web": "https://github.com/RFD-FHEM/RFFHEM/tree/master" }, "type": "svn", "url": "https://svn.fhem.de/fhem", "web": "https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/14_SD_WS07.pm", "x_branch": "trunk", "x_filepath": "fhem/FHEM/", "x_raw": "https://svn.fhem.de/trac/export/latest/trunk/fhem/FHEM/14_SD_WS07.pm" }, "x_support_community": { "board": "Sonstige Systeme", "boardId": "29", "cat": "FHEM - Hausautomations-Systeme", "description": "Sonstige Hausautomations-Systeme", "forum": "FHEM Forum", "rss": "https://forum.fhem.de/index.php?action=.xml;type=rss;board=29", "title": "FHEM Forum: Sonstige Systeme", "web": "https://forum.fhem.de/index.php/board,29.0.html" }, "x_wiki": { "web": "https://wiki.fhem.de/wiki/SIGNALduino" } } } =end :application/json;q=META.json =cut