diff --git a/FHEM/14_SD_WS09.pm b/FHEM/14_SD_WS09.pm index d3269b92a..156006183 100644 --- a/FHEM/14_SD_WS09.pm +++ b/FHEM/14_SD_WS09.pm @@ -1,577 +1,606 @@ - ############################################## - # $Id: 14_SD_WS09.pm 18672 2019-02-20 20:46:34Z Sidey $ - # - # 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 - # - # 22.09.2017: rainTotal --> rain_total - # 23.09.2017: windDirAverage SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 - # - # - - package main; - - use strict; - use warnings; - #use Math::Round qw/nearest/; - - # werden benötigt, aber im Programm noch extra abgetestet - #use Digest::CRC qw(crc); - #use Math::Trig; - - sub SD_WS09_Initialize($) - { - my ($hash) = @_; - - $hash->{Match} = "^P9#F[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 " - ."model:CTW600,WH1080 ignore:0,1 " - ."windKorrektur:-3,-2,-1,0,1,2,3 " - ."Unit_of_Wind:m/s,km/h,ft/s,mph,bft,knot " - ."WindDirAverageTime " - ."WindDirAverageMinSpeed " - ."WindDirAverageDecay " - ."$readingFnAttributes "; - $hash->{AutoCreate} = - { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 verbose:5" , FILTER => "%NAME", GPLOT => "WH1080wind4:windSpeed/windGust,", autocreateThreshold => "2:180"} }; - - - } - - ############################# - sub SD_WS09_Define($$) - { - my ($hash, $def) = @_; - my @a = split("[ \t][ \t]*", $def); - - return "wrong syntax: define SD_WS09 ".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 $model = $a[2]; - $model =~ s/_.*$//; - $hash->{MODEL} = $model; - - 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 %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); - my %uowind_index = ("m/s",'0',"km/h",'1',"ft/s",'2',"mph",'3',"knot",'4',"bft",'5'); - 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 $modelid; - my $windSpeed; - my $windSpeed_kmh; - my $windSpeed_fts; - my $windSpeed_bft; - my $windSpeed_mph; - my $windSpeed_kn; - my $windguest; - my $windguest_kmh; - my $windguest_fts; - my $windguest_bft; - my $windguest_mph; - my $windguest_kn; - my $sensdata; - my $id; - my $bat = 0; - my $temp = 0; - my $hum = 1; - my $windDirection = 1 ; - my $windDirectionText = "N"; - my $windDirectionDegree = 0; - my $FOuvo ; # UV data nybble ? - my $FOlux ; # Lux High byte (full scale = 4,000,000?) # Lux Middle byte # Lux Low byte, Unit = 0.1 Lux (binary) - my $rr2 ; - my $state; - my $msg_vor = 'P9#'; - my $minL1 = 70; - my $minL2 = 60; - my $whid; - my $wh; - my $rawData_merk; - my $wfaktor = 1; - my @windstat; - - my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble - Log3 $iohash, 4, "$name: SD_WS09_Parse0 msg=$rawData Bin=$bitData syncp=$syncpos length:".length($bitData) ; - - if ($syncpos ==-1 || length($bitData)-$syncpos < $minL2) - { - Log3 $iohash, 4, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ; - return undef; - } +############################################## +# $Id: 14_SD_WS09.pm 18672 2019-02-20 20:46:34Z Sidey $ +# +# The purpose of this module is to support serval +# weather sensors like WS-0101 (Sender 868MHz ASK Epmfänger RX868SH-DV elv) +# +# 2015 Sidey79, pejonp +# 2019 Ralf9 +# 2020 Sidey79, HomeAutoUser +# +# 20170922: rainTotal --> rain_total +# 20170923: windDirAverage - https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 @SabineT +# 20191003: UV/Solar Nachrichten WH2315 - https://forum.fhem.de/index.php/topic,67587.msg980092.html#msg980092 @Ralf +# 20200126: PERL WARNING - https://forum.fhem.de/index.php/topic,67587.msg982425.html#msg982425 @rob +# 20200127: Corrected line indents @HomeAutoUser +# 20200127: fix, WindDirAverage return undef --> return $windDirection_old @HomeAutoUser +# 20200127: revised commandref @HomeAutoUser +# + +package main; + +use strict; +use warnings; + +# werden benötigt, aber im Programm noch extra abgetestet +#use Digest::CRC qw(crc); +#use Math::Trig; + +sub SD_WS09_Initialize($) { + my ($hash) = @_; + + $hash->{Match} = "^P9#F[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 " + ."model:CTW600,WH1080 ignore:0,1 " + ."windKorrektur:-3,-2,-1,0,1,2,3 " + ."Unit_of_Wind:m/s,km/h,ft/s,mph,bft,knot " + ."WindDirAverageTime " + ."WindDirAverageMinSpeed " + ."WindDirAverageDecay " + ."$readingFnAttributes "; + $hash->{AutoCreate} = + { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 verbose:5" , FILTER => "%NAME", GPLOT => "WH1080wind4:windSpeed/windGust,", autocreateThreshold => "2:180"} }; +} + +############################# +sub SD_WS09_Define($$) { + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define SD_WS09 ".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 $model = $a[2]; + $model =~ s/_.*$//; + $hash->{MODEL} = $model; + + 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 %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); + my %uowind_index = ("m/s",'0',"km/h",'1',"ft/s",'2',"mph",'3',"knot",'4',"bft",'5'); + 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 $modelid; + my $windSpeed = 0; + my $windSpeed_kmh; + my $windSpeed_fts; + my $windSpeed_bft; + my $windSpeed_mph; + my $windSpeed_kn; + my $windguest = 0; + my $windguest_kmh; + my $windguest_fts; + my $windguest_bft; + my $windguest_mph; + my $windguest_kn; + my $sensdata; + my $id; + my $bat = 0; + my $temp = 0; + my $hum = 1; + my $windDirection = 1 ; + my $windDirectionText = "N"; + my $windDirectionDegree = 0; + my $FOuvo ; # UV data nybble ? + my $FOlux ; # Lux High byte (full scale = 4,000,000?) # Lux Middle byte # Lux Low byte, Unit = 0.1 Lux (binary) + my $rr2 ; + my $state; + my $msg_vor = 'P9#'; + my $minL1 = 70; + my $minL2 = 60; + my $whid; + my $wh; + my $rawData_merk; + my $wfaktor = 1; + my @windstat; + my $syncpos; - my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0); - Log3 $iohash, 4, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 " ; - $rawData_merk = $rawData; - - my $rc = eval - { - require Digest::CRC; + if ($hlen < 20) { # WH3080 UV/Solar + $syncpos = index($bitData,"1111110111"); # FF7 UV/Solar + if ($syncpos >= 0 && $syncpos < 3) { + $model = "WH1080"; + if ($syncpos < 2) { + $rawData = SD_WS09_SHIFT($iohash, $rawData); + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_0 raw:$rawData"; + } + if ($syncpos == 0) { + $rawData = SD_WS09_SHIFT($iohash, $rawData); + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_1 raw:$rawData"; + } + } else { + Log3 $iohash, 4, "$name: SD_WS09_Parse WH1080 EXIT (sync no found): msg=$rawData length:".length($bitData); + return ""; + } + } else { + $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble + Log3 $iohash, 4, "$name: SD_WS09_Parse0 msg=$rawData Bin=$bitData syncp=$syncpos length:".length($bitData) ; + + if ($syncpos ==-1 || length($bitData)-$syncpos < $minL2) { + Log3 $iohash, 4, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ; + return undef; + } + } + + my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0); + Log3 $iohash, 4, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 " ; + $rawData_merk = $rawData; + + my $rc = eval + { + require Digest::CRC; Digest::CRC->import(); 1; - }; + }; - if($rc) # test ob Digest::CRC geladen wurde - { - $rr2 = SD_WS09_CRCCHECK($rawData); - if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # test ob Digest::CRC geladen + if($rc) { + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($model eq "WH1080") { # FF7 UV/Solar + if ($rr2 != 0) { + Log3 $iohash, 4, "$name: SD_WS09_Parse WH1080: sensorTyp: 7 UV/Solar, CRC_Error Exit: raw:$rawData CRC=$rr2"; + return ""; + } + } else { + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { # 1. OK - $model = "WH1080"; - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_0 OK rwa:$rawData" ; + $model = "WH1080"; + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_0 OK raw:$rawData crc:$rr2"; } else { - # 1. nok - $rawData = SD_WS09_SHIFT($rawData); - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_1 NOK rwa:$rawData" ; - $rr2 = SD_WS09_CRCCHECK($rawData); - if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # 1. nok + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_1 NOK raw:$rawData crc:$rr2 -> shift"; + $rawData = SD_WS09_SHIFT($iohash, $rawData); + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { # 2.ok - $msg = $msg_vor.$rawData; - $model = "WH1080"; - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_2 OK rwa:$rawData msg:$msg" ; - } else { - # 2. nok - $rawData = SD_WS09_SHIFT($rawData); - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_3 NOK rwa:$rawData" ; - $rr2 = SD_WS09_CRCCHECK($rawData); - if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { - # 3. ok - $msg = $msg_vor.$rawData; - $model = "WH1080"; - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_4 OK rwa:$rawData msg:$msg" ; - }else{ - # 3. nok - $rawData = $rawData_merk; - $msg = $msg_vor.$rawData; - Log3 $iohash, 4, "$name: SD_WS09_SHIFT_5 NOK rwa:$rawData msg:$msg" ; - } - } - } - }else { - Log3 $iohash, 1, "$name: SD_WS09 CRC_not_load: Modul Digest::CRC fehlt: cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl" ; - return ""; - } - - $hlen = length($rawData); - $blen = $hlen * 4; - $bitData = unpack("B$blen", pack("H$hlen", $rawData)); - Log3 $iohash, 4, "$name: SD_WS09_CRC_test2 rwa:$rawData msg:$msg CRC:".SD_WS09_CRCCHECK($rawData) ; - - if( $model eq "WH1080") { - $sensdata = substr($bitData,8); - $whid = substr($sensdata,0,4); - - if( $whid == "1010" ){ # A Wettermeldungen - Log3 $iohash, 4, "$name: SD_WS09_Parse_1 msg=$sensdata 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]; - $windDirectionDegree = $windDirection * 360 / 16; - $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01); - Log3 $iohash, 4, "$name: SD_WS09_Parse_2 ".$model." id:$id, Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; - $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); - Log3 $iohash, 4, "$name: SD_WS09_Parse_3 ".$model." id:$id, Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; - $rain = SD_WS09_bin2dec(substr($sensdata,52,12)) * 0.3; - Log3 $iohash, 4, "$name: SD_WS09_Parse_4 ".$model." id:$id, Rain bit: ".substr($sensdata,52,12)." Dec: " . $rain ; - Log3 $iohash, 4, "$name: SD_WS09_Parse_5 ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain"; - } elsif( $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, 4, "$name: SD_WS09_Parse_6 Zeitmeldung0: HRS1=$hrs1 id:$id" ; - $hrs = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde - $mins = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute - $sec = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde - #day month year - $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr - $month = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat - $mday = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag - Log3 $iohash, 4, "$name: SD_WS09_Parse_7 Zeitmeldung1: id:$id, msg=$rawData length:".length($bitData) ; - Log3 $iohash, 4, "$name: SD_WS09_Parse_8 Zeitmeldung2: id:$id, HH:mm:ss - ".$hrs.":".$mins.":".$sec ; - Log3 $iohash, 4, "$name: SD_WS09_Parse_9 Zeitmeldung3: id:$id, dd.mm.yy - ".$mday.".".$month.".".$year ; - return $name; - } elsif( $whid == "0111" ){ # 7 UV/Solar Meldungen vom Sensor - # Fine Offset (Solar Data) message BYTE offsets (within receive buffer) - # Examples= FF 75 B0 55 00 97 8E 0E *CRC*OK* - # =FF 75 B0 55 00 8F BE 92 *CRC*OK* - # symbol FOrunio = 0 ; Fine Offset Runin byte = FF - # symbol FOsaddo = 1 ; Solar Pod address word - # symbol FOuvo = 3 ; UV data nybble ? - # symbol FOluxHo = 4 ; Lux High byte (full scale = 4,000,000?) - # symbol FOluxMo = 5 ; Lux Middle byte - # symbol FOluxLo = 6 ; Lux Low byte, Unit = 0.1 Lux (binary) - # symbol FOcksumo= 7 ; CRC checksum (CRC-8 shifting left) - $id = SD_WS09_bin2dec(substr($sensdata,4,8)); - $FOuvo = SD_WS09_bin2dec(substr($sensdata,12,4)); - $FOlux = SD_WS09_bin2dec(substr($sensdata,24,24))/10; - Log3 $iohash, 4, "$name: SD_WS09_Parse_10 UV-Solar1: id:$id, UV:".$FOuvo." Lux:".$FOlux ; - } else { - Log3 $iohash, 4, "$name: SD_WS09_Parse_Ex Exit: msg=$rawData length:".length($sensdata) ; - Log3 $iohash, 4, "$name: SD_WS09_WH10 Exit: Model=$model " ; - return undef; - } - }else{ - # es wird eine CTW600 angenommen - $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble - $wh = substr($bitData,0,8); - if ( $wh == "11111110" && length($bitData) > $minL1 ) - { - Log3 $iohash, 4, "$name: SD_WS09_Parse_11 CTW600 EXIT: msg=$bitData wh:$wh length:".length($bitData) ; - $sensdata = substr($bitData,$syncpos+8); - Log3 $iohash, 4, "$name: SD_WS09_Parse_12 CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ; - $model = "CTW600"; - $whid = "0000"; - my $nn1 = substr($sensdata,10,2); # Keine Bedeutung - my $nn2 = substr($sensdata,62,4); # Keine Bedeutung - $modelid = substr($sensdata,0,4); - Log3 $iohash, 4, "$name: SD_WS09_Parse_13 Id: ".$modelid." NN1:$nn1 NN2:$nn2" ; - Log3 $iohash, 4, "$name: SD_WS09_Parse_14 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]; - $windDirectionDegree = $windDirection * 360 / 16; - $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01); - Log3 $iohash, 4, "$name: SD_WS09_Parse_15 ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; - $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); - Log3 $iohash, 4, "$name: SD_WS09_Parse_16 ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; - $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01); - Log3 $iohash, 4, "$name: SD_WS09_Parse_17 ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ; - }else{ - Log3 $iohash, 4, "$name: SD_WS09_Parse_18 CTW600 EXIT: msg=$bitData length:".length($bitData) ; - return undef; - } - } - - - Log3 $iohash, 4, "$name: SD_WS09_Parse_19 ".$model." id:$id :$sensdata "; - - if($hum > 100 || $hum < 0) { - Log3 $iohash, 4, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ; - return undef; - } - if($temp > 60 || $temp < -40) { - Log3 $iohash, 4, "$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_20: $name ($rawData)"; - - if (!defined(AttrVal($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_End $deviceCode Dropped due to short time. minsecs=$minsecs"; - return undef; - } - } - - my $windkorr = AttrVal($name,'windKorrektur',0); - if ($windkorr != 0 ) - { - my $oldwinddir = $windDirection; - $windDirection = $windDirection + $windkorr; - $windDirectionText = $winddir_name[$windDirection]; - Log3 $hash, 4, "SD_WS09_Parse_WK ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ; - } - - # "Unit_of_Wind:m/s,km/h,ft/s,bft,knot " - # my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); - # B = Wurzel aus ( 9 + 6 * V ) - 3 - # V = 17 Meter pro Sekunde ergibt: B = Wurzel aus( 9 + 6 * 17 ) - 3 - # Das ergibt : 7,53 Beaufort - - $windstat[0]= " Ws:$windSpeed Wg:$windguest m/s"; - Log3 $hash, 4, "SD_WS09_Wind $windstat[0] : Faktor:$wfaktor" ; - - $wfaktor = $uowind_unit{"km/h"}; - $windguest_kmh = round ($windguest * $wfaktor,01); - $windSpeed_kmh = round ($windSpeed * $wfaktor,01); - $windstat[1]= " Ws:$windSpeed_kmh Wg:$windguest_kmh km/h"; - Log3 $hash, 4, "SD_WS09_Wind $windstat[1] : Faktor:$wfaktor" ; - - $wfaktor = $uowind_unit{"ft/s"}; - $windguest_fts = round ($windguest * $wfaktor,01); - $windSpeed_fts = round ($windSpeed * $wfaktor,01); - $windstat[2]= " Ws:$windSpeed_fts Wg:$windguest_fts ft/s"; - Log3 $hash, 4, "SD_WS09_Wind $windstat[2] : Faktor:$wfaktor" ; - - $wfaktor = $uowind_unit{"mph"}; - $windguest_mph = round ($windguest * $wfaktor,01); - $windSpeed_mph = round ($windSpeed * $wfaktor,01); - $windstat[3]= " Ws:$windSpeed_mph Wg:$windguest_mph mph"; - Log3 $hash, 4, "SD_WS09_Wind $windstat[3] : Faktor:$wfaktor" ; - - $wfaktor = $uowind_unit{"knot"}; - $windguest_kn = round ($windguest * $wfaktor,01); - $windSpeed_kn = round ($windSpeed * $wfaktor,01); - $windstat[4]= " Ws:$windSpeed_kn Wg:$windguest_kn kn" ; - Log3 $hash, 4, "SD_WS09_Wind $windstat[4] : Faktor:$wfaktor" ; - - $windguest_bft = round(sqrt( 9 + (6 * $windguest)) - 3,0) ; - $windSpeed_bft = round(sqrt( 9 + (6 * $windSpeed)) - 3,0) ; - $windstat[5]= " Ws:$windSpeed_bft Wg:$windguest_bft bft"; - Log3 $hash, 4, "SD_WS09_Wind $windstat[5] " ; - - # Resets des rain counters abfangen: - # wenn der aktuelle Wert < letzter Wert ist, dann fand ein reset statt - # die Differenz "letzer Wert - aktueller Wert" wird dann als offset für zukünftige Ausgaben zu rain addiert - # offset wird auch im Reading ".rain_offset" gespeichert - my $last_rain = ReadingsVal($name, "rain", 0); - my $rain_offset = ReadingsVal($name, ".rainOffset", 0); - $rain_offset += $last_rain if($rain < $last_rain); - my $rain_total = $rain + $rain_offset; - Log3 $hash, 4, "SD_WS09_Parse_rain_offset ".$model." rain:$rain raintotal:$rain_total rainoffset:$rain_offset " ; - - # windDirectionAverage berechnen - my $average = SD_WS09_WindDirAverage($hash, $windSpeed, $windDirectionDegree); - - $hash->{lastReceive} = time(); - $def->{lastMSG} = $rawData; - readingsBeginUpdate($hash); - - if($whid ne "0111") - { - #my $uowind = AttrVal($hash->{NAME},'Unit_of_Wind',0) ; - my $uowind = AttrVal($name,'Unit_of_Wind',0) ; - my $windex = $uowind_index{$uowind}; - if (!defined $windex) { - $windex = 0; - } - - $state = "T: $temp ". ($hum>0 ? " H: $hum ":" "). $windstat[$windex]." Wd: $windDirectionText "." R: $rain"; - readingsBulkUpdate($hash, "id", $id) if ($id ne ""); - 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, "batteryState", $bat) if ($bat ne ""); - - #zusätzlich Daten für Wetterstation - readingsBulkUpdate($hash, "rain", $rain ); - readingsBulkUpdate($hash, ".rainOffset", $rain_offset ); # Zwischenspeicher für den offset - readingsBulkUpdate($hash, "rain_total", $rain_total ); # monoton steigender Wert von rain - readingsBulkUpdate($hash, "windGust", $windguest ); - readingsBulkUpdate($hash, "windSpeed", $windSpeed ); - readingsBulkUpdate($hash, "windGust_kmh", $windguest_kmh ); - readingsBulkUpdate($hash, "windSpeed_kmh", $windSpeed_kmh ); - readingsBulkUpdate($hash, "windGust_fts", $windguest_fts ); - readingsBulkUpdate($hash, "windSpeed_fts", $windSpeed_fts ); - readingsBulkUpdate($hash, "windGust_mph", $windguest_mph ); - readingsBulkUpdate($hash, "windSpeed_mph", $windSpeed_mph ); - readingsBulkUpdate($hash, "windGust_kn", $windguest_kn ); - readingsBulkUpdate($hash, "windSpeed_kn", $windSpeed_kn ); - readingsBulkUpdate($hash, "windDirectionAverage", $average ); - readingsBulkUpdate($hash, "windDirection", $windDirection ); - readingsBulkUpdate($hash, "windDirectionDegree", $windDirectionDegree); - readingsBulkUpdate($hash, "windDirectionText", $windDirectionText ); - } - if(($whid eq "0111") && ($model eq "WH1080")) - { - $state = "UV: $FOuvo Lux: $FOlux "; - readingsBulkUpdate($hash, "id", $id) if ($id ne ""); - readingsBulkUpdate($hash, "state", $state); - #zusätzliche Daten UV + Lux - readingsBulkUpdate($hash, "UV", $FOuvo ); - readingsBulkUpdate($hash, "Lux", $FOlux ); - } - 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; - } + $msg = $msg_vor.$rawData; + $model = "WH1080"; + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_2 OK raw:$rawData msg:$msg crc:$rr2"; + } else { + # 2. nok + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_3 NOK raw:$rawData crc:$rr2 -> shift"; + $rawData = SD_WS09_SHIFT($iohash, $rawData); + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # 3. ok + $msg = $msg_vor.$rawData; + $model = "WH1080"; + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_4 OK raw:$rawData msg:$msg crc:$rr2"; + } else { + # 3. nok + $rawData = $rawData_merk; + $msg = $msg_vor.$rawData; + Log3 $iohash, 4, "$name: SD_WS09_Parse_SHIFT_5 NOK raw:$rawData msg:$msg crc:$rr2"; + } + } + } + } + } else { + # Digest::CRC failed + Log3 $iohash, 1, "$name: SD_WS09 CRC_not_load: Modul Digest::CRC fehlt: cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl" ; + return ""; + } - sub SD_WS09_WindDirAverage($$$){ - ############################################################################### - # übernommen von SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 - # WindDirAverage - # z.B.: myWindDirAverage('WH1080','windSpeed','windDirectionDegree',900,0.75,0.5) - # avtime ist optional, default ist 600 s Zeitspanne, die berücksichtig werden soll - # decay ist optional, default ist 1 Parameter, um ältere Werte geringer zu gewichten - # minspeed ist optional, default ist 0 m/s - # - # Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und - # vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. - # Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit - # bedeutet höhere Gewichtung). - # - # decay: 1 -> alle Werte werden gleich gewichtet - # 0 -> nur der aktuelle Wert wird verwendet. - # in der Praxis wird man Werte so um 0.75 nehmen - # - # minspeed: da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht - # eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden - # Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert - # - ############################################################################### - - my ($hash, $ws, $wd) = @_; - my $name = $hash->{NAME}; - Log3 $hash, 4, "SD_WS09_WindDirAverage --- OK ----" ; - my $rc = eval - { - require Math::Trig; - Math::Trig->import(); - 1; - }; - - if($rc) # test ob Math::Trig geladen wurde - { - Log3 $hash, 4, "SD_WS09_WindDirAverage Math::Trig:OK" ; - }else - { - Log3 $hash, 1, "SD_WS09_WindDirAverage Math::Trig:fehlt : cpan install Math::Trig" ; - return ""; - } - - my $avtime = AttrVal($name,'WindDirAverageTime',0); - my $decay = AttrVal($name,'WindDirAverageDecay',0); - my $minspeed = AttrVal($name,'WindDirAverageMinSpeed',0); - - # default Werte für die optionalen Parameter, falls nicht beim Aufruf mit angegeben - $avtime = 600 if (!(defined $avtime) || $avtime == 0 ); - $decay = 1 if (!(defined $decay)); - $decay = 1 if ($decay > 1); # darf nicht >1 sein - $decay = 0 if ($decay < 0); # darf nicht <0 sein - $minspeed = 0 if (!(defined $minspeed)); - - $wd = deg2rad($wd); - my $ctime = time; - my $time = FmtDateTime($ctime); - my @new = ($ws,$wd,$time); - - Log3 $hash, 4,"SD_WS09_WindDirAverage_01 $name :Speed=".$ws." DirR=".round($wd,2)." Time=".$time; - Log3 $hash, 4,"SD_WS09_WindDirAverage_02 $name :avtime=".$avtime." decay=".$decay." minspeed=".$minspeed; - - my $num; - my $arr; - - #-- initialize if requested - if( ($avtime eq "-1") ){ - $hash->{helper}{history}=undef; - } - - #-- test for existence - if(!$hash->{helper}{history}){ - Log3 $hash, 4,"SD_WS09_WindDirAverage_03 $name :ARRAY CREATED"; - push(@{$hash->{helper}{history}},\@new); - $num = 1; - $arr=\@{$hash->{helper}{history}}; - } else { - $num = int(@{$hash->{helper}{history}}); - $arr=\@{$hash->{helper}{history}}; - my $stime = time_str2num($arr->[0][2]); # Zeitpunkt des ältesten Eintrags - my $ltime = time_str2num($arr->[$num-1][2]); # Zeitpunkt des letzten Eintrags - Log3 $hash,4,"SD_WS09_WindDirAverage_04 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." ctime=".$ctime." ltime=".$ltime." stime=".$stime." num=".$num; - - if((($ctime - $ltime) > 10) || ($num == 0)) { - if(($num < 25) && (($ctime-$stime) < $avtime)){ - Log3 $hash,4,"SD_WS09_WindDirAverage_05 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; - push(@{$hash->{helper}{history}},\@new); - } else { - shift(@{$hash->{helper}{history}}); - push(@{$hash->{helper}{history}},\@new); - Log3 $hash,4,"SD_WS09_WindDirAverage_06 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; - } - } else { - return undef; - } - } - #-- output and average - my ($anz, $sanz) = 0; - $num = int(@{$hash->{helper}{history}}); - my ($sumSin, $sumCos, $sumSpeed, $age, $maxage, $weight) = 0; - for(my $i=0; $i<$num; $i++){ - ($ws, $wd, $time) = @{ $arr->[$i] }; - $age = $ctime - time_str2num($time); - if (($time eq "") || ($age > $avtime)) { - #-- zu alte Einträge entfernen + $hlen = length($rawData); + $blen = $hlen * 4; + $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + Log3 $iohash, 4, "$name: SD_WS09_CRC_test2 rwa:$rawData msg:$msg CRC:".SD_WS09_CRCCHECK($rawData) ; + + if( $model eq "WH1080") { + $sensdata = substr($bitData,8); + $whid = substr($sensdata,0,4); + Log3 $iohash, 5, "$name: SD_WS09_Parse_0 whid=$whid"; + + # A Wettermeldungen + if( $whid == "1010" ){ + Log3 $iohash, 4, "$name: SD_WS09_Parse_1 msg=$sensdata 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]; + $windDirectionDegree = $windDirection * 360 / 16; + $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_2 ".$model." id:$id, Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; + $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_3 ".$model." id:$id, Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; + $rain = SD_WS09_bin2dec(substr($sensdata,52,12)) * 0.3; + Log3 $iohash, 4, "$name: SD_WS09_Parse_4 ".$model." id:$id, Rain bit: ".substr($sensdata,52,12)." Dec: " . $rain ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_5 ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain"; + + # B DCF-77 Zeitmeldungen vom Sensor + } elsif ( $whid == "1011" ) { + 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, 4, "$name: SD_WS09_Parse_6 Zeitmeldung0: HRS1=$hrs1 id:$id" ; + $hrs = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde + $mins = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute + $sec = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde + #day month year + $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr + $month = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat + $mday = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag + Log3 $iohash, 4, "$name: SD_WS09_Parse_7 Zeitmeldung1: id:$id, msg=$rawData length:".length($bitData) ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_8 Zeitmeldung2: id:$id, HH:mm:ss - ".$hrs.":".$mins.":".$sec ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_9 Zeitmeldung3: id:$id, dd.mm.yy - ".$mday.".".$month.".".$year ; + return $name; + + # 7 UV/Solar Meldungen vom Sensor + } elsif ( $whid == "0111" ) { + # Fine Offset (Solar Data) message BYTE offsets (within receive buffer) + # Examples= FF 75 B0 55 00 97 8E 0E *CRC*OK* + # =FF 75 B0 55 00 8F BE 92 *CRC*OK* + # symbol FOrunio = 0 ; Fine Offset Runin byte = FF + # symbol FOsaddo = 1 ; Solar Pod address word + # symbol FOuvo = 3 ; UV data nybble ? + # symbol FOluxHo = 4 ; Lux High byte (full scale = 4,000,000?) + # symbol FOluxMo = 5 ; Lux Middle byte + # symbol FOluxLo = 6 ; Lux Low byte, Unit = 0.1 Lux (binary) + # symbol FOcksumo= 7 ; CRC checksum (CRC-8 shifting left) + $id = SD_WS09_bin2dec(substr($sensdata,4,8)); + $FOuvo = SD_WS09_bin2dec(substr($sensdata,12,4)); + $FOlux = SD_WS09_bin2dec(substr($sensdata,24,24))/10; + Log3 $iohash, 4, "$name: SD_WS09_Parse_10 UV-Solar1: id:$id, UV:".$FOuvo." Lux:".$FOlux ; + } else { + Log3 $iohash, 4, "$name: SD_WS09_Parse_Ex Exit: msg=$rawData length:".length($sensdata) ; + Log3 $iohash, 4, "$name: SD_WS09_WH10 Exit: Model=$model " ; + return undef; + } + } else { + # es wird eine CTW600 angenommen + $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble + $wh = substr($bitData,0,8); + if ( $wh == "11111110" && length($bitData) > $minL1 ) { + Log3 $iohash, 4, "$name: SD_WS09_Parse_11 CTW600 EXIT: msg=$bitData wh:$wh length:".length($bitData) ; + $sensdata = substr($bitData,$syncpos+8); + Log3 $iohash, 4, "$name: SD_WS09_Parse_12 CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ; + $model = "CTW600"; + $whid = "0000"; + my $nn1 = substr($sensdata,10,2); # Keine Bedeutung + my $nn2 = substr($sensdata,62,4); # Keine Bedeutung + $modelid = substr($sensdata,0,4); + Log3 $iohash, 4, "$name: SD_WS09_Parse_13 Id: ".$modelid." NN1:$nn1 NN2:$nn2" ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_14 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]; + $windDirectionDegree = $windDirection * 360 / 16; + $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_15 ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; + $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_16 ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; + $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_17 ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ; + } else { + Log3 $iohash, 4, "$name: SD_WS09_Parse_18 CTW600 EXIT: msg=$bitData length:".length($bitData) ; + return undef; + } + } + + + Log3 $iohash, 4, "$name: SD_WS09_Parse_19 ".$model." id:$id :$sensdata "; + + if($hum > 100 || $hum < 0) { + Log3 $iohash, 4, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ; + return undef; + } + if($temp > 60 || $temp < -40) { + Log3 $iohash, 4, "$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_20: $name ($rawData)"; + + if (!defined(AttrVal($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_End $deviceCode Dropped due to short time. minsecs=$minsecs"; + return undef; + } + } + + my $windkorr = AttrVal($name,'windKorrektur',0); + if ($windkorr != 0 ) { + my $oldwinddir = $windDirection; + $windDirection = $windDirection + $windkorr; + $windDirectionText = $winddir_name[$windDirection]; + Log3 $hash, 4, "SD_WS09_Parse_WK ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ; + } + + # "Unit_of_Wind:m/s,km/h,ft/s,bft,knot " + # my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); + # B = Wurzel aus ( 9 + 6 * V ) - 3 + # V = 17 Meter pro Sekunde ergibt: B = Wurzel aus( 9 + 6 * 17 ) - 3 + # Das ergibt : 7,53 Beaufort + + $windstat[0]= " Ws:$windSpeed Wg:$windguest m/s"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[0] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"km/h"}; + $windguest_kmh = round ($windguest * $wfaktor,01); + $windSpeed_kmh = round ($windSpeed * $wfaktor,01); + $windstat[1]= " Ws:$windSpeed_kmh Wg:$windguest_kmh km/h"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[1] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"ft/s"}; + $windguest_fts = round ($windguest * $wfaktor,01); + $windSpeed_fts = round ($windSpeed * $wfaktor,01); + $windstat[2]= " Ws:$windSpeed_fts Wg:$windguest_fts ft/s"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[2] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"mph"}; + $windguest_mph = round ($windguest * $wfaktor,01); + $windSpeed_mph = round ($windSpeed * $wfaktor,01); + $windstat[3]= " Ws:$windSpeed_mph Wg:$windguest_mph mph"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[3] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"knot"}; + $windguest_kn = round ($windguest * $wfaktor,01); + $windSpeed_kn = round ($windSpeed * $wfaktor,01); + $windstat[4]= " Ws:$windSpeed_kn Wg:$windguest_kn kn" ; + Log3 $hash, 4, "SD_WS09_Wind $windstat[4] : Faktor:$wfaktor" ; + + $windguest_bft = round(sqrt( 9 + (6 * $windguest)) - 3,0) ; + $windSpeed_bft = round(sqrt( 9 + (6 * $windSpeed)) - 3,0) ; + $windstat[5]= " Ws:$windSpeed_bft Wg:$windguest_bft bft"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[5] " ; + + # Resets des rain counters abfangen: + # wenn der aktuelle Wert < letzter Wert ist, dann fand ein reset statt + # die Differenz "letzer Wert - aktueller Wert" wird dann als offset für zukünftige Ausgaben zu rain addiert + # offset wird auch im Reading ".rain_offset" gespeichert + my $last_rain = ReadingsVal($name, "rain", 0); + my $rain_offset = ReadingsVal($name, ".rainOffset", 0); + $rain_offset += $last_rain if($rain < $last_rain); + my $rain_total = $rain + $rain_offset; + Log3 $hash, 4, "SD_WS09_Parse_rain_offset ".$model." rain:$rain raintotal:$rain_total rainoffset:$rain_offset " ; + + # windDirectionAverage berechnen + my $average = SD_WS09_WindDirAverage($hash, $windSpeed, $windDirectionDegree); + + $hash->{lastReceive} = time(); + $def->{lastMSG} = $rawData; + readingsBeginUpdate($hash); + + if($whid ne "0111") { + #my $uowind = AttrVal($hash->{NAME},'Unit_of_Wind',0) ; + my $uowind = AttrVal($name,'Unit_of_Wind',0) ; + my $windex = $uowind_index{$uowind}; + if (!defined $windex) { + $windex = 0; + } + + $state = "T: $temp ". ($hum>0 ? " H: $hum ":" "). $windstat[$windex]." Wd: $windDirectionText "." R: $rain"; + readingsBulkUpdate($hash, "id", $id) if ($id ne ""); + 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, "batteryState", $bat) if ($bat ne ""); + + #zusätzlich Daten für Wetterstation + readingsBulkUpdate($hash, "rain", $rain ); + readingsBulkUpdate($hash, ".rainOffset", $rain_offset ); # Zwischenspeicher für den offset + readingsBulkUpdate($hash, "rain_total", $rain_total ); # monoton steigender Wert von rain + readingsBulkUpdate($hash, "windGust", $windguest ); + readingsBulkUpdate($hash, "windSpeed", $windSpeed ); + readingsBulkUpdate($hash, "windGust_kmh", $windguest_kmh ); + readingsBulkUpdate($hash, "windSpeed_kmh", $windSpeed_kmh ); + readingsBulkUpdate($hash, "windGust_fts", $windguest_fts ); + readingsBulkUpdate($hash, "windSpeed_fts", $windSpeed_fts ); + readingsBulkUpdate($hash, "windGust_mph", $windguest_mph ); + readingsBulkUpdate($hash, "windSpeed_mph", $windSpeed_mph ); + readingsBulkUpdate($hash, "windGust_kn", $windguest_kn ); + readingsBulkUpdate($hash, "windSpeed_kn", $windSpeed_kn ); + readingsBulkUpdate($hash, "windDirectionAverage", $average ); + readingsBulkUpdate($hash, "windDirection", $windDirection ); + readingsBulkUpdate($hash, "windDirectionDegree", $windDirectionDegree); + readingsBulkUpdate($hash, "windDirectionText", $windDirectionText ); + } + + if (($whid eq "0111") && ($model eq "WH1080")) { + $state = "UV: $FOuvo Lux: $FOlux "; + readingsBulkUpdate($hash, "id", $id) if ($id ne ""); + readingsBulkUpdate($hash, "state", $state); + #zusätzliche Daten UV + Lux + readingsBulkUpdate($hash, "UV", $FOuvo ); + readingsBulkUpdate($hash, "Lux", $FOlux ); + } + + 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_WindDirAverage($$$){ + ############################################################################### + # übernommen von SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 + # WindDirAverage + # z.B.: myWindDirAverage('WH1080','windSpeed','windDirectionDegree',900,0.75,0.5) + # avtime ist optional, default ist 600 s Zeitspanne, die berücksichtig werden soll + # decay ist optional, default ist 1 Parameter, um ältere Werte geringer zu gewichten + # minspeed ist optional, default ist 0 m/s + # + # Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und + # vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. + # Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit + # bedeutet höhere Gewichtung). + # + # decay: 1 -> alle Werte werden gleich gewichtet + # 0 -> nur der aktuelle Wert wird verwendet. + # in der Praxis wird man Werte so um 0.75 nehmen + # + # minspeed: da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht + # eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden + # Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert + # + ############################################################################### + + my ($hash, $ws, $wd) = @_; + my $name = $hash->{NAME}; + Log3 $hash, 4, "SD_WS09_WindDirAverage --- OK ----" ; + + my $rc = eval + { + require Math::Trig; + Math::Trig->import(); + 1; + }; + + if($rc) # test ob Math::Trig geladen wurde + { + Log3 $hash, 4, "SD_WS09_WindDirAverage Math::Trig:OK" ; + } else { + Log3 $hash, 1, "SD_WS09_WindDirAverage Math::Trig:fehlt : cpan install Math::Trig" ; + return ""; + } + + my $avtime = AttrVal($name,'WindDirAverageTime',0); + my $decay = AttrVal($name,'WindDirAverageDecay',0); + my $minspeed = AttrVal($name,'WindDirAverageMinSpeed',0); + my $windDirection_old = $wd; + + # default Werte für die optionalen Parameter, falls nicht beim Aufruf mit angegeben + $avtime = 600 if (!(defined $avtime) || $avtime == 0 ); + $decay = 1 if (!(defined $decay)); + $decay = 1 if ($decay > 1); # darf nicht >1 sein + $decay = 0 if ($decay < 0); # darf nicht <0 sein + $minspeed = 0 if (!(defined $minspeed)); + + $wd = deg2rad($wd); + my $ctime = time; + my $time = FmtDateTime($ctime); + my @new = ($ws,$wd,$time); + + Log3 $hash, 4,"SD_WS09_WindDirAverage_01 $name :Speed=".$ws." DirR=".round($wd,2)." Time=".$time; + Log3 $hash, 4,"SD_WS09_WindDirAverage_02 $name :avtime=".$avtime." decay=".$decay." minspeed=".$minspeed; + + my $num; + my $arr; + + #-- initialize if requested + if( ($avtime eq "-1") ){ + $hash->{helper}{history}=undef; + } + + #-- test for existence + if(!$hash->{helper}{history}){ + Log3 $hash, 4,"SD_WS09_WindDirAverage_03 $name :ARRAY CREATED"; + push(@{$hash->{helper}{history}},\@new); + $num = 1; + $arr=\@{$hash->{helper}{history}}; + } else { + $num = int(@{$hash->{helper}{history}}); + $arr=\@{$hash->{helper}{history}}; + my $stime = time_str2num($arr->[0][2]); # Zeitpunkt des ältesten Eintrags + my $ltime = time_str2num($arr->[$num-1][2]); # Zeitpunkt des letzten Eintrags + Log3 $hash,4,"SD_WS09_WindDirAverage_04 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." ctime=".$ctime." ltime=".$ltime." stime=".$stime." num=".$num; + + if((($ctime - $ltime) > 10) || ($num == 0)) { + if(($num < 25) && (($ctime-$stime) < $avtime)){ + Log3 $hash,4,"SD_WS09_WindDirAverage_05 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; + push(@{$hash->{helper}{history}},\@new); + } else { + shift(@{$hash->{helper}{history}}); + push(@{$hash->{helper}{history}},\@new); + Log3 $hash,4,"SD_WS09_WindDirAverage_06 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; + } + } else { + return $windDirection_old; # old undef | different behavior dispatch <-> UnitTest´s ($ctime - $ltime) + } + } + + #-- output and average + my ($anz, $sanz) = 0; + $num = int(@{$hash->{helper}{history}}); + my ($sumSin, $sumCos, $sumSpeed, $age, $maxage, $weight) = 0; + for(my $i=0; $i<$num; $i++){ + ($ws, $wd, $time) = @{ $arr->[$i] }; + $age = $ctime - time_str2num($time); + if (($time eq "") || ($age > $avtime)) { + #-- zu alte Einträge entfernen Log3 $hash,4,"SD_WS09_WindDirAverage_07 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." ctime=".$ctime." akt.=".time_str2num($time); shift(@{$hash->{helper}{history}}); $i--; $num--; } else { - #-- Werte aufsummieren, Windrichtung gewichtet über Geschwindigkeit und decay/"alter" + #-- Werte aufsummieren, Windrichtung gewichtet über Geschwindigkeit und decay/"alter" $weight = $decay ** ($age / $avtime); #-- für die Mittelwertsbildung der Geschwindigkeit wird nur ein 10tel von avtime genommen if ($age < ($avtime / 10)) { @@ -586,69 +615,75 @@ } my $average = int((rad2deg(atan2($sumSin, $sumCos)) + 360) % 360); Log3 $hash,4,"SD_WS09_WindDirAverage_09 $name Mittelwert über $anz Werte ist $average, avspeed=".round($sumSpeed/$num,1) if ($num > 0); - #-- undef zurückliefern, wenn die durchschnittliche Geschwindigkeit zu gering oder gar keine Werte verfügbar + + #-- undef zurückliefern, wenn die durchschnittliche Geschwindigkeit zu gering oder gar keine Werte verfügbar return undef if (($anz == 0) || ($sanz == 0)); return undef if (($sumSpeed / $sanz) < $minspeed); - Log3 $hash,4,"SD_WS09_WindDirAverage_END $name Mittelwert=$average"; + + Log3 $hash,4,"SD_WS09_WindDirAverage_END $name Mittelwert=$average"; return $average; } - - 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; - } - - sub SD_WS09_SHIFT($){ - my $rawData = shift; - my $hlen = length($rawData); - my $blen = $hlen * 4; - my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); - my $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData)); - my $bitData20 = substr($bitData2,0,length($bitData2)-1); - $blen = length($bitData20); - $hlen = $blen / 4; - $rawData = uc(unpack("H$hlen", pack("B$blen", $bitData20))); - $bitData = $bitData20; - Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_0 raw: $rawData length:".length($bitData) ; - Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_1 bitdata: $bitData" ; - return $rawData; - } - - sub SD_WS09_CRCCHECK($) { - my $rawData = shift; - my $datacheck1 = pack( 'H*', substr($rawData,2,length($rawData)-2) ); - my $crcmein1 = Digest::CRC->new(width => 8, poly => 0x31); - my $rr3 = $crcmein1->add($datacheck1)->hexdigest; - $rr3 = sprintf("%d", hex($rr3)); - Log3 "SD_WS09_CRCCHECK", 4, "SD_WS09_CRCCHECK : raw:$rawData CRC=$rr3 " ; - return $rr3 ; - } - - 1; - - + +################################### +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; +} + +################################### +sub SD_WS09_SHIFT($$){ + my ($hash, $rawData) = @_; + my $name = $hash->{NAME}; + my $hlen = length($rawData); + my $blen = $hlen * 4; + my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + my $bitData2 = '1'.$bitData; + my $bitData20 = substr($bitData2,0,length($bitData2)-1); + $blen = length($bitData20); + $hlen = $blen / 4; + $rawData = uc(unpack("H$hlen", pack("B$blen", $bitData20))); + $bitData = $bitData20; + #Log3 $hash, 4, "$name: SD_WS09_SHIFT_0 raw: $rawData length:".length($bitData); + Log3 $hash, 5, "$name: SD_WS09_SHIFT_1 bitdata: $bitData length:".length($bitData); + return $rawData; +} + +################################### +sub SD_WS09_CRCCHECK($) { + my $rawData = shift; + my $datacheck1 = pack( 'H*', substr($rawData,2,length($rawData)-2) ); + my $crcmein1 = Digest::CRC->new(width => 8, poly => 0x31); + my $rr3 = $crcmein1->add($datacheck1)->hexdigest; + $rr3 = sprintf("%d", hex($rr3)); + Log3 "SD_WS09_CRCCHECK", 4, "SD_WS09_CRCCHECK : raw:$rawData CRC=$rr3 " ; + return $rr3 ; +} + +1; + + =pod =item summary Supports weather sensors (WH1080/3080/CTW-600) protocl 9 from SIGNALduino =item summary_DE Unterstützt Wettersensoren (WH1080/3080/CTW-600) mit Protokol 9 vom SIGNALduino @@ -657,19 +692,21 @@

Wether Sensors protocol #9

    - The SD_WS09 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.
    - Requires Perl-Modul Digest::CRC.
    -
    - cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl
    -
    + The SD_WS09 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.

    + Requires Perl-Modul Digest::CRC / Math::Trig
    +
      +
    • cpan install Digest::CRC or
      + sudo apt-get install libdigest-crc-perl
    • +
    • cpan install Math::Trig


    Known models:
      -
    • WS-0101 --> Model: WH1080
    • +
    • WS-0101 --> Model: WH1080
    • TFA 30.3189 / WH1080 --> Model: WH1080
    • -
    • 1073 (WS1080) --> Model: WH1080
    • -
    • WH3080 --> Model: WH1080
    • -
    • CTW600 --> Model: CTW600 (??)
    • +
    • 1073 (WS1080) --> Model: WH1080
    • +
    • WH2315 --> Model: WH1080
    • +
    • WH3080 --> Model: WH1080
    • +
    • CTW600 --> Model: CTW600

    New received device are add in fhem with autocreate. @@ -751,8 +788,8 @@
  • windDirectionAverage
    As a result, the wind direction is returned, which are calculated from the current and past values via a kind of exponential mean value. - The respective wind speed is additionally taken into account (higher speed means higher weighting)
  • - WH3080: + The respective wind speed is additionally taken into account (higher speed means higher weighting)
    + WH2315 / WH3080:
  • UV Index
  • Lux
  • @@ -767,19 +804,20 @@
      Das SD_WS09 Module verarbeitet von einem IO Gerät (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.

      - Perl-Modul Digest::CRC erforderlich.
      -
      - cpan install Digest::CRC oder auch
      - sudo apt-get install libdigest-crc-perl
      -
      + Perl-Module Digest::CRC / Math::Trig erforderlich.
      +
        +
      • cpan install Digest::CRC oder auch
        + sudo apt-get install libdigest-crc-perl
      • +
      • cpan install Math::Trig


      - Unterstütze Modelle: + Unterstütze Modelle:
        -
      • WS-0101 --> Model: WH1080
      • +
      • WS-0101 --> Model: WH1080
      • TFA 30.3189 / WH1080 --> Model: WH1080
      • -
      • 1073 (WS1080) --> Model: WH1080
      • -
      • WH3080 --> Model: WH1080
      • -
      • CTW600 --> Model: CTW600
      • +
      • 1073 (WS1080) --> Model: WH1080
      • +
      • WH2315 --> Model: WH1080
      • +
      • WH3080 --> Model: WH1080
      • +
      • CTW600 --> Model: CTW600

      Neu empfangene Sensoren werden in FHEM per autocreate angelegt. @@ -789,7 +827,7 @@ Define
        Die empfangenen Sensoren werden automatisch angelegt.
        Die ID der angelegten Sensoren wird nach jedem Batteriewechsel geändert, welche der Sensor beim Einschalten zufällig vergibt.
        - CRC Checksumme wird zur Zeit noch nicht überprüft, deshalb werden Sensoren bei denen die Luftfeuchte < 0 oder > 100 ist, nicht angelegt.
        + CRC Checksumme wird zur Zeit noch nicht überprüft, deshalb werden Sensoren bei denen die Luftfeuchte < 0 oder > 100 ist, nicht angelegt.

      @@ -825,14 +863,14 @@
    • WindDirAverageTime
      - default ist 600s, Zeitspanne die für die Berechung berücksichtig werden soll + default ist 600s, Zeitspanne die für die Berechung berücksichtig werden soll

    • WindDirAverageMinSpeed
      - da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht + da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden - Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert + Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert

    • @@ -845,7 +883,7 @@
    • WS09_CRCAUS
      Wird im Signalduino-Modul (00_SIGNALduino.pm) gesetzt -
      0: CRC-Prüfung bei WH1080 CRC-Summe = 0 +
      0: CRC-Prüfung bei WH1080 CRC-Summe = 0
      2: CRC-Summe = 49 (x031) bei WH1080 wird als OK verarbeitet


    @@ -861,11 +899,11 @@
  • windSpeed/windgust (Einheit siehe Unit_of_Wind) und windDirection (N-O-S-W)
  • Rain (mm)
  • windDirectionAverage - Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und - vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. - Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit - bedeutet höhere Gewichtung).
  • - WH3080: + Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und + vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. + Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit + bedeutet höhere Gewichtung).
    + WH2315 / WH3080:
  • UV Index
  • Lux
  • diff --git a/UnitTest/tests/test_SD_WS09_devices-definition.txt b/UnitTest/tests/test_SD_WS09_devices-definition.txt new file mode 100644 index 000000000..c94dc1403 --- /dev/null +++ b/UnitTest/tests/test_SD_WS09_devices-definition.txt @@ -0,0 +1,95 @@ +defmod test_SD_WS09_devices UnitTest dummyDuino ( +{ + use Test2::V0; + use Test2::Tools::Compare qw{is}; + use Test2::Require::Module 'Digest::CRC'; # need for SD_WS09 modul - SD_WS09_CRCCHECK + use Test2::Require::Module 'Math::Trig'; # need for SD_WS09 modul - SD_WS09_WindDirAverage + use JSON; + + ## for online testsytem: defmod test_SDWS09_devices UnitTest dummyDuino ## + subtest "check requirements" => sub { + plan(1); + CommandAttr(undef,"global dupTimeout 0"); + is(AttrVal("global", "dupTimeout", undef),0,"dupTimeout is set to 0"); + }; + + my $testDataArray; + sub loadJson { + my $jsonStr=GetFileFromURL($_[0],4,"",1,4); + $testDataArray = eval { decode_json($jsonStr) }; + if($@){ + diag("open json file TestData was not possible $?"); + diag explain $jsonStr; + } + } + + my $jsonSourceUrl = AttrVal($name,"Test_Data_URI","https://raw.githubusercontent.com/RFD-FHEM/SIGNALduino_TOOL/master/FHEM/lib/SD_Device_ProtocolList.json"); + loadJson($jsonSourceUrl); + + my $todo = undef; + TestLabel: + while ( (my $pID, my $testSet) = each (@{$testDataArray}) ) { + next if (!defined($testSet->{module}) || $testSet->{module} ne "SD_WS09"); + next if (!lib::SD_Protocols::exists($testSet->{id})); + next if (lib::SD_Protocols::checkProperty($testSet->{id},'developId',undef)); + + while ( (my $tID, my $tData) = each (@{$testSet->{data}}) ) { + #diag explain $tData; + + subtest "Checking module: $testSet->{module} device: $testSet->{name} TestNo: $tID " => sub { + + if (IsDevice($tData->{internals}{NAME})) { + plan(3); # one for readings and one for internals + note("device is already defined on system, clearing readings"); + CommandDeleteReading(undef, "$tData->{internals}{NAME} .*"); + } else { + plan(4); # one for readings and one for internals and one for defmod + note("device will be defined temporary"); + is(CommandDefMod(undef,"-temporary $tData->{internals}{NAME} $testSet->{module} $tData->{internals}{DEF}"),U(),"Verify device defmod") || diag explain $tData; + } + + ### note ### + ## readings must all deleted, any DMSG create only some readings and not all + ## without delete, always all readings exists + subtest "check deleted readings" => sub { + my $cnt=0; + while ( (my $rName, my $rValue) = each (%{$defs{$testSet->{name}}->{READINGS}}) ) { + $cnt++; + } + plan(1); + is($cnt,0,"all readings deleted"); + }; + + no strict "refs"; + &{$modules{$testSet->{module}}{ParseFn}}($targetHash,$tData->{dmsg}); # Handover DMSG + use strict "refs"; + + subtest "Verify readings" => sub { + my $plan=0; + while ( (my $rName, my $rValue) = each (%{$tData->{readings}}) ) { + $plan++; + is(ReadingsVal($tData->{internals}{NAME} ,$rName,"0"),$rValue,"check reading $rName"); + } + plan($plan); + }; + + subtest "Verify internals" => sub { + my $plan=0; + while ( (my $iName, my $iValue) = each (%{$tData->{internals}}) ) { + $plan++; + is(InternalVal($tData->{internals}{NAME} ,$iName,"0"),$iValue,"check internal $iName"); + } + plan($plan); + }; + } + } + } + + if ( !defined($todo) ) { + my $jsonSourceUrl = AttrVal($name,"Test_Data_URI","https://raw.githubusercontent.com/RFD-FHEM/SIGNALduino_TOOL/pre-release/FHEM/lib/SD_Device_ProtocolList.json"); + loadJson($jsonSourceUrl); + $todo = todo "Testing against pre-release testdata, correct test before push this to master"; + goto TestLabel; + } +} +) diff --git a/cpanfile b/cpanfile index 0ee3ed021..fc62a6b38 100644 --- a/cpanfile +++ b/cpanfile @@ -8,4 +8,6 @@ requires 'Test::More'; requires 'Test::Device::SerialPort'; requires 'Devel::Cover'; requires 'Devel::Cover::Report::Coveralls'; -requires 'Net::SSLeay'; \ No newline at end of file +requires 'Net::SSLeay'; +requires 'Digest::CRC'; +requires 'Math::Trig'; \ No newline at end of file