| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- ##############################################
- ##############################################
- # $Id: 14_SD_WS09.pm 12233 2016-10-01 22:22:51Z mrsidey $
- #
- # The purpose of this module is to support serval
- # weather sensors like WS-0101 (Sender 868MHz ASK Epmfänger RX868SH-DV elv)
- # Sidey79 & pejonp 2015
- #
- package main;
-
- use strict;
- use warnings;
- use Digest::CRC qw(crc);
-
- #use Math::Round qw/nearest/;
-
- sub SD_WS09_Initialize($)
- {
- my ($hash) = @_;
-
- $hash->{Match} = "^P9#[A-Fa-f0-9]+"; ## pos 7 ist aktuell immer 0xF
- $hash->{DefFn} = "SD_WS09_Define";
- $hash->{UndefFn} = "SD_WS09_Undef";
- $hash->{ParseFn} = "SD_WS09_Parse";
- $hash->{AttrFn} = "SD_WS09_Attr";
- $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 "
- ."windKorrektur:-3,-2,-1,0,1,2,3 "
- ."$readingFnAttributes ";
- $hash->{AutoCreate} =
- { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 " , FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} };
-
-
- }
-
- #############################
- sub SD_WS09_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
-
- return "wrong syntax: define <name> SD_WS09 <code> ".int(@a)
- if(int(@a) < 3 );
-
- $hash->{CODE} = $a[2];
- $hash->{lastMSG} = "";
- $hash->{bitMSG} = "";
-
- $modules{SD_WS09}{defptr}{$a[2]} = $hash;
- $hash->{STATE} = "Defined";
-
- my $name= $hash->{NAME};
- return undef;
- }
-
- #####################################
- sub SD_WS09_Undef($$)
- {
- my ($hash, $name) = @_;
- delete($modules{SD_WS09}{defptr}{$hash->{CODE}})
- if(defined($hash->{CODE}) &&
- defined($modules{SD_WS09}{defptr}{$hash->{CODE}}));
- return undef;
- }
-
-
- ###################################
- sub SD_WS09_Parse($$)
- {
- my ($iohash, $msg) = @_;
- my $name = $iohash->{NAME};
- my (undef ,$rawData) = split("#",$msg);
- my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW");
- my $hlen = length($rawData);
- my $blen = $hlen * 4;
- my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
- my $bitData2;
- my $bitData20;
- my $rain = 0;
- my $deviceCode = 0;
- my $model = "undef"; # 0xFFA -> WS0101/WH1080 alles andere -> CTW600
- my $modelattr ;
- my $modelid;
- my $windSpeed = 0;
- my $windguest =0;
- my $sensdata;
- my $id;
- my $bat = 0;
- my $temp;
- my $hum;
- my $windDirection ;
- my $windDirectionText;
-
-
- $modelattr = AttrVal($iohash->{NAME},'WS09_WSModel',0);
- if ($modelattr eq '0'){
- $modelattr = "undef";
- }
-
- my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0);
- Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 Model=$modelattr" ;
-
- my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble
- Log3 $iohash, 3, "$name: SD_WS09_Parse0 Bin=$bitData syncp=$syncpos length:".length($bitData) ;
- if ($syncpos ==-1 || length($bitData)-$syncpos < 78)
- {
- Log3 $iohash, 3, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ;
- return undef;
- }
- my $wh = substr($bitData,0,8);
- #CRC-Check bei WH1080/WS0101 WS09_CRCAUS=0 und WS09_WSModel = undef oder Wh1080
- if(($crcwh1080 == 0) && ($modelattr ne "CTW600")) {
- if($wh == "11111111") {
- if ($syncpos == 0)
- {
- $hlen = length($rawData);
- $blen = $hlen * 4;
- $bitData2 = '11'.unpack("B$blen", pack("H$hlen", $rawData));
- $bitData20 = substr($bitData2,0,length($bitData2)-2);
- $blen = length($bitData20);
- $hlen = $blen / 4;
- $msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20));
- $bitData = $bitData20;
- Log3 $iohash, 3, "$name: SD_WS09_Parse sync1 msg=$msg syncp=$syncpos length:".length($bitData) ;
- }
-
- if ($syncpos == 1)
- {
- $hlen = length($rawData);
- $blen = $hlen * 4;
- $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData));
- $bitData20 = substr($bitData2,0,length($bitData2)-1);
- $blen = length($bitData20);
- $hlen = $blen / 4;
- $msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20));
- $bitData = $bitData20;
- Log3 $iohash, 3, "$name: SD_WS09_Parse sync2 msg=$msg syncp=$syncpos length:".length($bitData) ;
- }
-
-
- my $datacheck = pack( 'H*', substr($msg,5,length($msg)-5) );
- my $crcmein = Digest::CRC->new(width => 8, poly => 0x31);
- my $rr2 = $crcmein->add($datacheck)->hexdigest;
- if ($rr2 eq "0"){
- $model = "WH1080";
- Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_OK: CRC=$rr2 Model=$model attr=$modelattr" ;
- }else{
- Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_Error: msg=$msg CRC=$rr2 " ;
- return undef;
- }
- }else{
- $model = "CTW600";
- Log3 $iohash, 3, "$name: SD_WS09_Parse CTW600: Model=$model attr=$modelattr" ;
- }
- };
-
- if( ($wh == "11111111") || ($model eq "WH1080")) {
- if ($modelattr eq "CTW600"){
- Log3 $iohash, 3, "$name: SD_WS09_WH1080 off=$modelattr Model=$model " ;
- return undef;
- }
- $sensdata = substr($bitData,8);
- my $whid = substr($sensdata,0,4);
- if( $whid == "1010" ){ # A
- Log3 $iohash, 3, "$name: SD_WS09_Parse WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ;
- $model = "WH1080";
- $id = SD_WS09_bin2dec(substr($sensdata,4,8));
- $bat = (SD_WS09_bin2dec((substr($sensdata,64,4))) == 0) ? 'ok':'low' ; # decode battery = 0 --> ok
- $temp = (SD_WS09_bin2dec(substr($sensdata,12,12)) - 400)/10;
- $hum = SD_WS09_bin2dec(substr($sensdata,24,8));
- $windDirection = SD_WS09_bin2dec(substr($sensdata,68,4));
- $windDirectionText = $winddir_name[$windDirection];
- $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01);
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
- $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
- $rain = SD_WS09_bin2dec(substr($sensdata,56,8)) * 0.3;
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,56,8)." Dec: " . $rain ;
- } else {
- if( $whid == "1011" ){ # B DCF-77 Zeitmeldungen vom Sensor
- my $hrs1 = substr($sensdata,16,8);
- my $hrs;
- my $mins;
- my $sec;
- my $mday;
- my $month;
- my $year;
- $id = SD_WS09_bin2dec(substr($sensdata,4,8));
- Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung0: HRS1=$hrs1 id:$id" ;
- $hrs = SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde
- $mins = SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute
- $sec = SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde
- #day month year
- $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr
- $month = SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat
- $mday = SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag
- Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung1: msg=$rawData syncp=$syncpos length:".length($bitData) ;
- Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung2: HH:mm:ss - ".$hrs.":".$mins.":".$sec ;
- Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung3: dd.mm.yy - ".$mday.":".$month.":".$year ;
- return $name;
- }
- Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung4: msg=$rawData syncp=$syncpos length:".length($sensdata) ;
- return undef;
- }
- }else{
- if ($modelattr eq "WH1080"){
- Log3 $iohash, 3, "$name: SD_WS09_CTW600 off=$modelattr Model=$model " ;
- return undef;
- } else {
- # eine CTW600 wurde erkannt
- $sensdata = substr($bitData,$syncpos+8);
- Log3 $iohash, 3, "$name: SD_WS09_Parse CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ;
- $model = "CTW600";
- my $nn1 = substr($sensdata,10,2); # Keine Bedeutung
- my $nn2 = substr($sensdata,62,4); # Keine Bedeutung
- $modelid = substr($sensdata,0,4);
- Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." NN1:$nn1 NN2:$nn2" ;
- Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." Bin-Sync=$sensdata syncp=$syncpos length:".length($sensdata) ;
- $bat = SD_WS09_bin2dec((substr($sensdata,0,3))) ;
- $id = SD_WS09_bin2dec(substr($sensdata,4,6));
- $temp = (SD_WS09_bin2dec(substr($sensdata,12,10)) - 400)/10;
- $hum = SD_WS09_bin2dec(substr($sensdata,22,8));
- $windDirection = SD_WS09_bin2dec(substr($sensdata,66,4));
- $windDirectionText = $winddir_name[$windDirection];
- $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01);
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
- $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
- $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01);
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ;
- }
- }
-
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." id:$id :$sensdata ";
- Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain";
-
- if($hum > 100 || $hum < 0) {
- Log3 $iohash, 3, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ;
- return undef;
- }
- if($temp > 60 || $temp < -40) {
- Log3 $iohash, 3, "$name: SD_WS09_Parse TEMP: Temp=$temp msg=$rawData " ;
- return undef;
- }
-
-
- my $longids = AttrVal($iohash->{NAME},'longids',0);
- if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/)))
- {
- $deviceCode=$model."_".$id;
- Log3 $iohash,4, "$name: SD_WS09_Parse using longid: $longids model: $model";
- } else {
- $deviceCode = $model;
- }
-
- my $def = $modules{SD_WS09}{defptr}{$iohash->{NAME} . "." . $deviceCode};
- $def = $modules{SD_WS09}{defptr}{$deviceCode} if(!$def);
-
- if(!$def) {
- Log3 $iohash, 1, 'SD_WS09_Parse UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode;
- return "UNDEFINED $deviceCode SD_WS09 $deviceCode";
- }
-
- my $hash = $def;
- $name = $hash->{NAME};
- Log3 $name, 4, "SD_WS09_Parse: $name ($rawData)";
-
-
- my $windkorr = AttrVal($hash->{NAME},'windKorrektur',0);
- if ($windkorr != 0 )
- {
- my $oldwinddir = $windDirection;
- $windDirection = $windDirection + $windkorr;
- $windDirectionText = $winddir_name[$windDirection];
- Log3 $iohash, 3, "SD_WS09_Parse ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ;
- }
-
- 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, "SD_WS09_Parse $deviceCode Dropped due to short time. minsecs=$minsecs";
- return "";
- }
- }
-
- $def->{lastMSG} = $rawData;
-
- my $state = "T: $temp ". ($hum>0 ? " H: $hum ":" ")." Ws: $windSpeed "." Wg: $windguest "." Wd: $windDirectionText "." R: $rain";
-
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "state", $state);
- readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne"");
- readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "" && $hum != 0 );
- readingsBulkUpdate($hash, "battery", $bat) if ($bat ne "");
- readingsBulkUpdate($hash, "id", $id) if ($id ne "");
-
- #zusätzlich Daten für Wetterstation
- readingsBulkUpdate($hash, "rain", $rain );
- readingsBulkUpdate($hash, "windGust", $windguest );
- readingsBulkUpdate($hash, "windSpeed", $windSpeed );
- readingsBulkUpdate($hash, "windDirection", $windDirection );
- readingsBulkUpdate($hash, "windDirectionDegree", $windDirection * 360 / 16);
- readingsBulkUpdate($hash, "windDirectionText", $windDirectionText );
- readingsEndUpdate($hash, 1); # Notify is done by Dispatch
-
- return $name;
-
- }
-
- sub SD_WS09_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_WS09}{defptr}{$cde});
- $modules{SD_WS09}{defptr}{$iohash->{NAME} . "." . $cde} = $hash;
- return undef;
- }
-
-
- sub SD_WS09_bin2dec($)
- {
- my $h = shift;
- my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32)));
- return sprintf("%d", $int);
- }
- sub SD_WS09_binflip($)
- {
- my $h = shift;
- my $hlen = length($h);
- my $i = 0;
- my $flip = "";
-
- for ($i=$hlen-1; $i >= 0; $i--) {
- $flip = $flip.substr($h,$i,1);
- }
-
- return $flip;
- }
-
-
- sub SD_WS09_BCD2bin($) {
- my $binary = shift;
- my $int = unpack("N", pack("B32", substr("0" x 32 . $binary, -32)));
- my $BCD = sprintf("%x", $int );
- return $BCD;
- }
-
-
-
- 1;
-
-
- =pod
- =item summary Supports weather sensors protocl 9 from SIGNALduino
- =item summary_DE Unterstützt Wettersensoren mit Protokol 9 vom SIGNALduino
- =begin html
- <a name="SD_WS09"></a>
- <h3>Wether Sensors protocol #9</h3>
- <ul>
- The SD_WS09 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.<br>
- Requires Perl-Modul Digest::CRC. <br>
- <br>
- cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl <br>
- <br>
- <br>
- <b>Known models:</b>
- <ul>
- <li>WS-0101 --> Model: WH1080</li>
- <li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
- <li>1073 (WS1080) --> Model: WH1080</li>
- <li>CTW600 --> Model: CTW600 (??) </li>
- </ul>
- <br>
- New received device are add in fhem with autocreate.
- <br><br>
- <a name="SD_WS09_Define"></a>
- <b>Define</b>
- <ul>The received devices created automatically.<br>
- The ID of the defice is the model or, if the longid attribute is specified, it is a combination of model and some random generated bits at powering the sensor.<br>
- If you want to use more sensors, you can use the longid option to differentiate them.
- </ul>
- <br>
- <a name="SD_WS09 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: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, windDirection, Rain</li>
- <li>Temperature (°C)</li>
- <li>Humidity: (The humidity (1-100 if available)</li>
- <li>Battery: (low or ok)</li>
- <li>ID: (The ID-Number (number if)</li>
- <li>windSpeed (m/s) and windDirection (N-O-S-W)</li>
- <li>Rain (mm)</li>
- </ul>
- <br>
- <b>Attributes</b>
- <ul>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#showtime">showtime</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <li>Model<br>
- WH1080, CTW600
- </li><br>
- <li>windKorrektur<br>
- -3,-2,-1,0,1,2,3
- </li><br>
- </ul>
- <a name="SD_WS09_Set"></a>
- <b>Set</b> <ul>N/A</ul><br>
- <a name="SD_WS09_Parse"></a>
- <b>Set</b> <ul>N/A</ul><br>
- </ul>
- =end html
- =begin html_DE
- <a name="SD_WS09"></a>
- <h3>SD_WS09</h3>
- <ul>
- Das SD_WS09 Module verarbeitet von einem IO Gerät (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.<br>
- <br>
- Perl-Modul Digest::CRC erforderlich. <br>
- <br>
- cpan install Digest::CRC oder auch <br>
- sudo apt-get install libdigest-crc-perl <br>
- <br>
- <br>
- <b>Unterstütze Modelle:</b>
- <ul>
- <li>WS-0101 --> Model: WH1080</li>
- <li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
- <li>1073 (WS1080) --> Model: WH1080</li>
- <li>CTW600 --> Model: CTW600 (nicht getestet) </li>
- </ul>
- <br>
- Neu empfangene Sensoren werden in FHEM per autocreate angelegt.
- <br><br>
- <a name="SD_WS09_Define"></a>
- <b>Define</b>
- <ul>Die empfangenen Sensoren werden automatisch angelegt.<br>
- Die ID der angelegten Sensoren wird nach jedem Batteriewechsel geändert, welche der Sensor beim Einschalten zufällig vergibt.<br>
- CRC Checksumme wird zur Zeit noch nicht überprüft, deshalb werden Sensoren bei denen die Luftfeuchte < 0 oder > 100 ist, nicht angelegt.<br>
- </ul>
- <br>
- <a name="SD_WS09 Events"></a>
- <b>Generierte Readings:</b>
- <ul>
- <li>State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, windDirection, Rain</li>
- <li>Temperature (°C)</li>
- <li>Humidity: (The humidity (1-100 if available)</li>
- <li>Battery: (low or ok)</li>
- <li>ID: (The ID-Number (number if)</li>
- <li>windSpeed (m/s) and windDirection (N-O-S-W)</li>
- <li>Rain (mm)</li>
- </ul>
- <br>
- <b>Attribute</b>
- <ul>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#showtime">showtime</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <li>Model<br>
- WH1080, CTW600
- </li><br>
- <li>windKorrektur<br>
- Korrigiert die Nord-Ausrichtung des Windrichtungsmessers, wenn dieser nicht richtig nach Norden ausgerichtet ist.
- -3,-2,-1,0,1,2,3
- </li><br>
- </ul>
- <a name="SD_WS09_Set"></a>
- <b>Set</b> <ul>N/A</ul><br>
- <a name="SD_WS09_Parse"></a>
- <b>Set</b> <ul>N/A</ul><br>
- </ul>
- =end html_DE
- =cut
|