fhem-mirror/FHEM/14_SD_WS09.pm
sidey79 25e12c3413 14_SD_WS09.pm: compare operator fixes
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@22415 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2020-07-16 20:22:23 +00:00

917 lines
34 KiB
Perl
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

##############################################
# $Id$
#
# 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 <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 $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;
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;
};
# 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_Parse_SHIFT_0 OK raw:$rawData crc:$rr2";
} else {
# 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_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 "";
}
$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 eq '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 eq '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 eq '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 eq '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"
$weight = $decay ** ($age / $avtime);
#-- für die Mittelwertsbildung der Geschwindigkeit wird nur ein 10tel von avtime genommen
if ($age < ($avtime / 10)) {
$sumSpeed += $ws * $weight if ($age < ($avtime / 10));
$sanz++;
}
$sumSin += sin($wd) * $ws * $weight;
$sumCos += cos($wd) * $ws * $weight;
$anz++;
Log3 $hash,4,"SD_WS09_WindDirAverage_08 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." vec=".round($sumSin,2)."/".round($sumCos,2)." age=".$age." ".round($weight,2);
}
}
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
return undef if (($anz == 0) || ($sanz == 0));
return undef if (($sumSpeed / $sanz) < $minspeed);
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 ($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 Unterstuetzt Wettersensoren (WH1080/3080/CTW-600) / 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><br>
Requires Perl-Modul Digest::CRC / Math::Trig <br>
<ul>
<li>cpan install Digest::CRC or <br>
sudo apt-get install libdigest-crc-perl </li>
<li>cpan install Math::Trig </li></ul><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>WH2315 --> Model: WH1080</li>
<li>WH3080 --> Model: WH1080</li>
<li>CTW600 --> Model: CTW600</li>
</ul>
<br>
New received device are add in fhem with autocreate.
<br><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_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS09_Get"></a>
<b>Get</b> <ul>N/A</ul><br><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>
<a name="Model"></a>
<li>Model: WH1080,CTW600
</li>
<a name="windKorrektur"></a>
<li>windKorrektur: -3,-2,-1,0,1,2,3
</li>
<a name="Unit_of_Wind"></a>
<li>Unit_of_Wind<br>
Unit of windSpeed and windGuest. State-Format: Value + Unit.
<br>m/s,km/h,ft/s,mph,bft,knot
</li><br>
<a name="WindDirAverageTime"></a>
<li>WindDirAverageTime<br>
default is 600s, time span to be considered for the calculation
</li><br>
<a name="WindDirAverageMinSpeed"></a>
<li>WindDirAverageMinSpeed<br>
since the wind direction is usually not clear at very low wind speeds,
minspeed can be used to specify a threshold value.
<br>The (weighted) mean velocity < minspeed is returned undef
</li><br>
<a name="WindDirAverageDecay"></a>
<li>WindDirAverageDecay<br>
1 -> all values are weighted equally <br>
0 -> only the current value is used. <br>
in practice, you will take values around 0.75
</li><br>
<a name="WS09_CRCAUS"></a>
<li>WS09_CRCAUS (set in Signalduino-Modul 00_SIGNALduino.pm)
<br>0: CRC-Check WH1080 CRC-Summe = 0 on, default
<br>2: CRC-Summe = 49 (x031) WH1080, set OK
</li>
</ul> <br><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 (&deg;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/windGuest (Unit_of_Wind)) and windDirection (N-O-S-W)</li>
<li>Rain (mm)</li>
<li>windDirectionAverage<br>
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)</li><br>
<b>WH2315 / WH3080:</b>
<li>UV Index</li>
<li>Lux</li>
</ul>
</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-Module Digest::CRC / Math::Trig erforderlich. <br>
<ul>
<li>cpan install Digest::CRC oder auch <br>
sudo apt-get install libdigest-crc-perl </li>
<li>cpan install Math::Trig </li></ul><br>
<br>
<b>Unterst&uuml;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>WH2315 --> Model: WH1080</li>
<li>WH3080 --> Model: WH1080</li>
<li>CTW600 --> Model: CTW600</li>
</ul>
<br>
Neu empfangene Sensoren werden in FHEM per autocreate angelegt.
<br><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&aumlndert, welche der Sensor beim Einschalten zuf&aumlllig vergibt.<br>
CRC Checksumme wird zur Zeit noch nicht &uuml;berpr&uumlft, deshalb werden Sensoren bei denen die Luftfeuchte < 0 oder > 100 ist, nicht angelegt.<br>
</ul>
<br>
<a name="SD_WS09_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS09_Get"></a>
<b>Get</b> <ul>N/A</ul><br><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>
<a name="Model"></a>
<li>Model<br>
WH1080, CTW600
</li><br>
<a name="windKorrektur"></a>
<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>
<a name="Unit_of_Wind"></a>
<li>Unit_of_Wind<br>
Hiermit wird der Einheit eingestellt und im State die entsprechenden Werte + Einheit angezeigt.
<br>m/s,km/h,ft/s,mph,bft,knot
</li><br>
<a name="WindDirAverageTime"></a>
<li>WindDirAverageTime<br>
default ist 600s, Zeitspanne die f&uuml;r die Berechung ber&uuml;cksichtig werden soll
</li><br>
<a name="WindDirAverageMinSpeed"></a>
<li>WindDirAverageMinSpeed<br>
da bei sehr geringer Windgeschwindigkeit die Windrichtung &uuml;blicherweise nicht
eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden
Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zur&uuml;ck geliefert
</li><br>
<a name="WindDirAverageDecay"></a>
<li>WindDirAverageDecay<br>
1 -> alle Werte werden gleich gewichtet <br>
0 -> nur der aktuelle Wert wird verwendet.<br>
in der Praxis wird man Werte so um 0.75 nehmen
</li><br>
<a name="WS09_CRCAUS"></a>
<li>WS09_CRCAUS<br>
Wird im Signalduino-Modul (00_SIGNALduino.pm) gesetzt
<br>0: CRC-Pr&uuml;fung bei WH1080 CRC-Summe = 0
<br>2: CRC-Summe = 49 (x031) bei WH1080 wird als OK verarbeitet
</li><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, Einheit, windDirection, Rain</li>
<li>Temperature (&deg;C)</li>
<li>Humidity: (1-100 wenn verf&uuml;gbar)</li>
<li>Battery: (low or ok)</li>
<li>ID: (ID-Nummer wenn verf&uuml;gbar)</li>
<li>windSpeed/windgust (Einheit siehe Unit_of_Wind) und windDirection (N-O-S-W)</li>
<li>Rain (mm)</li>
<li>windDirectionAverage
Als Ergebnis wird die Windrichtung zur&uuml;ck geliefert, die aus dem aktuellen und
vergangenen Werten &uuml;ber eine Art exponentiellen Mittelwert berechnet werden.
Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit ber&uuml;cksichtigt (h&ouml;here Geschwindigkeit
bedeutet h&ouml;here Gewichtung).</li><br>
<b>WH2315 / WH3080:</b>
<li>UV Index</li>
<li>Lux</li>
</ul>
</ul>
=end html_DE
=cut