2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-22 20:24:36 +00:00

00_SIGNALduino.pm: Version 3.3.2 with many fixes

support of blacklisting of protocols via new attribute
 serval new protocols are added
 many stacktraces and race conditions are fixed
 firmware can now be also updated via http source 
 commands (sending) are now placed in a queue, so switching multiple 
 sockets for example is now possible withoud manual added delay
 many more fixes
 support for cc1101 based uC like radino or nanocul added

14_Hideki.pm: (windsensor support added)

14_SD_WS: (new tfa sensor supported)

14_SD_WS07: (extraction of rssi value support)

14_SD_WS09: (wind/rain total support)

git-svn-id: https://svn.fhem.de/fhem/trunk@15450 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
sidey79 2017-11-18 21:34:47 +00:00
parent 391248d007
commit 8879eb3973
5 changed files with 3075 additions and 610 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
# see http://www.fhemwiki.de/wiki/SIGNALduino
# and was modified by a few additions
# to support Hideki Sensors
# S. Butzek & HJGode & Ralf9 2015-2016
# S. Butzek, HJGode, Ralf9 2015-2017
#
package main;
@ -27,8 +27,9 @@ Hideki_Initialize($)
$hash->{UndefFn} = "Hideki_Undef";
$hash->{AttrFn} = "Hideki_Attr";
$hash->{ParseFn} = "Hideki_Parse";
$hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 "
."ignore:0,1 "
$hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1"
." ignore:0,1"
." windDirCorr windSpeedCorr"
." $readingFnAttributes";
$hash->{AutoCreate}=
@ -53,7 +54,7 @@ Hideki_Define($$)
my $name= $hash->{NAME};
$modules{Hideki}{defptr}{$a[2]} = $hash;
$hash->{STATE} = "Defined";
#$hash->{STATE} = "Defined";
#AssignIoPort($hash);
return undef;
@ -102,25 +103,64 @@ Hideki_Parse($$)
}
my $id=substr($decodedString,2,2); # get the random id from the data
my $channel=0;
my $temp=0;
my $channel=0;
my $temp="";
my $hum=0;
my $rain=0;
my $unknown=0;
my $windchill=0;
my $windspeed=0;
my $windgust=0;
my $winddir=0;
my $winddirdeg=0;
my $winddirtext;
my $rc;
my $val;
my $bat;
my $deviceCode;
my $model= "Hideki_$sensorTyp";
my $count=0;
my $comfort=0;
## 1. Detect what type of sensor we have, then call specific function to decode
if ($sensorTyp==0x1E){
($channel, $temp, $hum) = decodeThermoHygro(\@decodedBytes); # decodeThermoHygro($decodedString);
if ($sensorTyp==30){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString);
$hum = 10 * ($decodedBytes[6] >> 4) + ($decodedBytes[6] & 0x0f);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$val = "T: $temp H: $hum Bat: $bat";
}elsif($sensorTyp==0x0E){
$count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter
$comfort = ($decodedBytes[7] >> 2 & 0x03); # comfort level
if ($comfort == 0) { $comfort = 'Hum. OK. Temp. uncomfortable (>24.9 or <20)' }
elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' }
elsif ($comfort == 2) { $comfort = 'Dry. Less than 40% RH' }
elsif ($comfort == 3) { $comfort = 'Temp. and Hum. comfortable' }
$val = "T: $temp H: $hum";
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp, humidity=$hum, comfort=$comfort";
}elsif($sensorTyp==31){
($channel, $temp) = decodeThermo(\@decodedBytes);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter
$val = "T: $temp";
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp";
}elsif($sensorTyp==14){
($channel, $rain) = decodeRain(\@decodedBytes); # decodeThermoHygro($decodedString);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$val = "R: $rain Bat: $bat";
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "R: $rain";
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, rain=$rain, unknown=$unknown";
}elsif($sensorTyp==12){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString);
($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedBytes);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "T: $temp Ws: $windspeed Wg: $windgust Wd: $winddirtext";
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp, Wc=$windchill, Ws=$windspeed, Wg=$windgust, Wd=$winddir, WdDeg=$winddirdeg, Wdtxt=$winddirtext";
}elsif($sensorTyp==13){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "T: $temp";
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp";
Log3 $iohash, 4, "$name Sensor Typ $sensorTyp currently not full supported, please report sensor information!";
}
else{
Log3 $iohash, 4, "$name Sensor Typ $sensorTyp not supported, please report sensor information!";
@ -135,14 +175,13 @@ Hideki_Parse($$)
$deviceCode = $model . "_" . $channel;
}
Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, temp=$temp, humidity=$hum, bat=$bat, rain=$rain";
Log3 $iohash, 5, "deviceCode: $deviceCode";
my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode};
$def = $modules{Hideki}{defptr}{$deviceCode} if(!$def);
if(!$def) {
Log3 $iohash, 1, "Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode";
Log3 $iohash, 1, "$name Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode";
return "UNDEFINED $deviceCode Hideki $deviceCode";
}
@ -152,6 +191,12 @@ Hideki_Parse($$)
#Log3 $name, 4, "Hideki: $name ($msg)";
my $WindSpeedCorr = AttrVal($name,"windSpeedCorr",0);
if ($WindSpeedCorr > 0 && $sensorTyp == 12) {
$windspeed = sprintf("%.2f", $windspeed * $WindSpeedCorr);
$windgust = sprintf("%.2f", $windgust * $WindSpeedCorr);
Log3 $name, 4, "$name Hideki_Parse: WindSpeedCorr=$WindSpeedCorr, WindSpeed=$windspeed, WindGust=$windgust";
}
if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef)))
{
@ -171,13 +216,24 @@ Hideki_Parse($$)
readingsBulkUpdate($hash, "state", $val);
readingsBulkUpdate($hash, "battery", $bat) if ($bat ne "");
readingsBulkUpdate($hash, "channel", $channel) if ($channel ne "");
if ($sensorTyp==0x1E){
readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "");
readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne "");
readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne "");
readingsBulkUpdate($hash, "package_number", $count) if ($count ne "");
if ($sensorTyp == 30) { # temperature, humidity
readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "");
readingsBulkUpdate($hash, "comfort_level", $comfort) if ($comfort ne "");
}
elsif($sensorTyp==0x0E){
readingsBulkUpdate($hash, "rain", $rain) if ($rain ne "");
elsif ($sensorTyp == 14) { # rain
readingsBulkUpdate($hash, "rain", $rain);
}
elsif ($sensorTyp == 12) { # wind
readingsBulkUpdate($hash, "windChill", $windchill);
readingsBulkUpdate($hash, "windGust", $windgust);
readingsBulkUpdate($hash, "windSpeed", $windspeed);
readingsBulkUpdate($hash, "windDirection", $winddir);
readingsBulkUpdate($hash, "windDirectionDegree", $winddirdeg);
readingsBulkUpdate($hash, "windDirectionText", $winddirtext);
}
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
return $name;
@ -227,6 +283,7 @@ sub Hideki_crc{
# 0x0D UV sensor
# 0x0E Rain level meter
# 0x1E Thermo/hygro-sensor
# 0x1F Thermo sensor
sub getSensorType{
return $_[0] & 0x1F;
}
@ -254,11 +311,11 @@ sub Hideki_decryptByte{
return $ret2;
}
# decode byte array and return channel, temperature and hunidity
# decode byte array and return channel, temperature
# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array);
# output <return code>, <channel>, <temperature>, <humidity>
# output <return code>, <channel>, <temperature>
# was unable to get this working with an array ref as input, so switched to hex string input
sub decodeThermoHygro {
sub decodeThermo {
my @Hidekibytes = @{$_[0]};
#my $Hidekihex = shift;
@ -269,7 +326,6 @@ sub decodeThermoHygro {
#}
my $channel=0;
my $temp=0;
my $humi=0;
$channel = $Hidekibytes[1] >> 5;
# //Internally channel 4 is used for the other sensor types (rain, uv, anemo).
@ -285,12 +341,11 @@ sub decodeThermoHygro {
$temp = -$temp;
}
$humi = 10 * ($Hidekibytes[6] >> 4) + ($Hidekibytes[6] & 0x0f);
$temp = $temp / 10;
return ($channel, $temp, $humi);
return ($channel, $temp);
}
# decode byte array and return channel and total rain in mm
# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array);
# output <return code>, <channel>, <totalrain>
@ -306,18 +361,20 @@ sub decodeRain {
#}
my $channel=0;
my $rain=0;
my $unknown;
my $tests=0;
#my $tests=0;
#additional checks?
if($Hidekibytes[2]==0xCC){
$tests+=1;
}
if($Hidekibytes[6]==0x66){
$tests+=1;
}
#if($Hidekibytes[2]==0xCC){
# $tests+=1;
#}
#if($Hidekibytes[6]==0x66){
# $tests+=1;
#}
# possibly test if $tests==2 for sanity check
#printf("SANITY CHECK tests=%i\n", $tests);
$unknown = $Hidekibytes[6];
$channel = $Hidekibytes[1] >> 5;
# //Internally channel 4 is used for the other sensor types (rain, uv, anemo).
# //Therefore, if channel is decoded 5 or 6, the real value set on the device itself is 4 resp 5.
@ -328,7 +385,33 @@ sub decodeRain {
$rain = ($Hidekibytes[4] + $Hidekibytes[5]*0xff)*0.7;
return ($channel, $rain);
return ($channel, $rain, $unknown);
}
# P12#758BB244074007400F00001C6E7A01
sub wind {
my @Hidekibytes = @{$_[0]};
my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW");
my $windspeed;
my $windchill;
my $windgust;
my $winddir;
my $winddirdeg;
my $winddirtext;
$windchill = 100 * ($Hidekibytes[7] & 0x0f) + 10 * ($Hidekibytes[6] >> 4) + ($Hidekibytes[6] & 0x0f);
## windchill is negative?
if (!($Hidekibytes[7] & 0x80)) {
$windchill = -$windchill;
}
$windchill = $windchill / 10;
$windspeed = ($Hidekibytes[9] & 0x0f ) * 100 + ($Hidekibytes[8] >> 4) * 10 + ($Hidekibytes[8] & 0x0f);
$windgust = ($Hidekibytes[10] >> 4) * 100 + ($Hidekibytes[10] & 0x0f) * 10 + ($Hidekibytes[9] >> 4);
$winddir = ($Hidekibytes[11] >> 4);
$winddirtext = $winddir_name[$winddir];
$winddirdeg = $winddir * 22.5;
return ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext);
}
sub
@ -369,7 +452,7 @@ Hideki_Attr(@)
<li>TFA Dostman</li>
<li>Arduinos with remote Sensor lib from Randy Simons</li>
<li>Cresta</li>
<li>Hideki</li>
<li>Hideki (Anemometer | UV sensor | Rain level meter | Thermo/hygro-sensor)</li>
<li>all other devices, which use the Hideki protocol</li>
</ul>
Please note, currently temp/hum devices are implemented. Please report data for other sensortypes.
@ -395,6 +478,9 @@ Hideki_Attr(@)
<li>humidity (0-100)</li>
<li>battery (ok or low)</li>
<li>channel (The Channelnumber (number if)</li>
<br><i>- Hideki only -</i>
<li>comfort_level (Status: Humidity OK... , Wet. More than 69% RH, Dry. Less than 40% RH, Temperature and humidity comfortable)</li>
<li>package_number (reflect the package number in the stream starting at 1)</li><br>
</ul>
@ -434,7 +520,7 @@ Hideki_Attr(@)
<li>TFA Dostman</li>
<li>Arduinos with remote Sensor lib from Randy Simons</li>
<li>Cresta</li>
<li>Hideki</li>
<li>Hideki (Anemometer | UV sensor | Rain level meter | Thermo/hygro-sensor)</li>
<li>Alle anderen, welche das Hideki Protokoll verwenden</li>
</ul>
Hinweis, Aktuell sind nur temp/feuchte Sensoren implementiert. Bitte sendet uns Daten zu anderen Sensoren.
@ -454,13 +540,16 @@ Hideki_Attr(@)
<br>
<a name="Hideki_readings"></a>
<b>Erzeugte Readings</b>
<b>Generated Readings</b>
<ul>
<li>state (T:x H:y B:z)</li>
<li>temperature (&deg;C)</li>
<li>humidity (0-100)</li>
<li>battery (ok or low)</li>
<li>battery (ok oder low)</li>
<li>channel (Der Sensor Kanal)</li>
<br><i>- Hideki spezifisch -</i>
<li>comfort_level (Status: Humidity OK... , Wet größer 69% RH, Dry weiniger als 40% RH, Temperature and humidity comfortable)</li>
<li>package_number (Paketnummer in der letzten Signalfolge, startet bei 1)</li><br>
</ul>
<a name="Hideki_unset"></a>
<b>Set</b> <ul>N/A</ul><br>

View File

@ -3,16 +3,19 @@
#
# The purpose of this module is to support serval
# weather sensors which use various protocol
# Sidey79 & Ralf9 2016
#
# Sidey79 & Ralf9 2016 - 2017
# Joerg 2017
# 17.04.2017 WH2 (TFA 30.3157 nur Temp, Hum = 255),es wird das Perlmodul Digest:CRC benoetigt fuer CRC-Pruefung benoetigt
# 29.05.2017 Test ob Digest::CRC installiert
# 22.07.2017 WH2 angepasst
# 21.08.2017 WH2 Abbruch wenn kein "FF" am Anfang
package main;
use strict;
use warnings;
#use Data::Dumper;
# use Digest::CRC qw(crc);
# use Data::Dumper;
sub SD_WS_Initialize($)
@ -25,13 +28,16 @@ sub SD_WS_Initialize($)
$hash->{ParseFn} = "SD_WS_Parse";
$hash->{AttrFn} = "SD_WS_Attr";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " .
"$readingFnAttributes ";
"$readingFnAttributes ";
$hash->{AutoCreate} =
{
"SD_WS37_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"},
"SD_WS50_SM.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"},
"BresserTemeo.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}
"BresserTemeo.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"},
"SD_WS51_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"},
"SD_WS58_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:90"},
"SD_WH2.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:90"},
"SD_WS71_T.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"},
};
}
@ -63,7 +69,7 @@ sub SD_WS_Undef($$)
{
my ($hash, $name) = @_;
delete($modules{SD_WS}{defptr}{$hash->{CODE}})
if(defined($hash->{CODE}) && defined($modules{SD_WS}{defptr}{$hash->{CODE}}));
if(defined($hash->{CODE}) && defined($modules{SD_WS}{defptr}{$hash->{CODE}}));
return undef;
}
@ -77,8 +83,7 @@ sub SD_WS_Parse($$)
my ($protocol,$rawData) = split("#",$msg);
$protocol=~ s/^[WP](\d+)/$1/; # extract protocol
my $dummyreturnvalue= "Unknown, please report";
my $hlen = length($rawData);
my $blen = $hlen * 4;
@ -113,74 +118,166 @@ sub SD_WS_Parse($$)
prematch => sub {my $msg = shift; return 1 if ($msg =~ /^FF5[0-9A-F]{5}FF[0-9A-F]{2}/); }, # prematch
crcok => sub {my $msg = shift; return 1 if ((hex(substr($msg,2,2))+hex(substr($msg,4,2))+hex(substr($msg,6,2))+hex(substr($msg,8,2))&0xFF) == (hex(substr($msg,10,2))) ); }, # crc
id => sub {my $msg = shift; return (hex(substr($msg,2,2)) &0x03 ); }, #id
#temp => sub {my $msg = shift; return (sprintf('%x',((hex(substr($msg,6,2)) <<4)/2/10))); }, #temp
#temphex => sub {my $msg = shift; return sprintf("%04X",((hex(substr($msg,6,2)))<<4)/2); }, #temp
temp => sub {my $msg = shift; return ((hex(substr($msg,6,2)))-40) }, #temp
#hum => sub {my $msg = shift; return (printf('%02x',hex(substr($msg,4,2)))); }, #hum
hum => sub {my $msg = shift; return hex(substr($msg,4,2)); }, #hum
channel => sub {my (undef,$bitData) = @_; return ( SD_WS_binaryToNumber($bitData,12,15)&0x03 ); }, #channel
bat => sub { return "";},
},
71 =>
# 5C2A909F792F
# 589A829FDFF4
# PiiTTTK?CCCC
# P = Preamble (immer 5 ?)
# i = ID
# T = Temperatur
# K = Kanal (B/A/9)
# ? = immer F ?
# C = Checksum ?
{
sensortype => 'PV-8644',
model => 'SD_WS71_T',
prematch => sub {my $msg = shift; return 1 if ($msg =~ /^5[A-F0-9]{6}F[A-F0-9]{2}/); }, # prematch
crcok => sub {return 1; }, # crc is unknown
id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,4,11); }, # id
temp => sub {my (undef,$bitData) = @_; return ((SD_WS_binaryToNumber($bitData,12,23) - 2448) / 10); }, #temp
channel => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,26,27); }, #channel
hum => sub {return undef;},
bat => sub {return undef;},
},
33 =>
{
sensortype => 's014/TFA 30.3200/TCM/Conrad',
model => 'SD_WS_33_TH',
prematch => sub {my $msg = shift; return 1 if ($msg =~ /^[0-9A-F]{10,11}/); }, # prematch
crcok => sub {return SD_WS_binaryToNumber($bitData,36,39); }, # crc
crcok => sub {return SD_WS_binaryToNumber($bitData,36,39)+1; }, # crc currently not calculated
id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,0,9); }, # id
# sendmode => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,10,11) eq "1" ? "manual" : "auto"; }
temp => sub {my (undef,$bitData) = @_; return (((SD_WS_binaryToNumber($bitData,22,25)*256 + SD_WS_binaryToNumber($bitData,18,21)*16 + SD_WS_binaryToNumber($bitData,14,17)) *10 -12200) /18)/10; }, #temp
hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,30,33)*16 + SD_WS_binaryToNumber($bitData,26,29)); }, #hum
channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,12,13)+1 ); }, #channel
bat => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,34) eq "1" ? "ok" : "critical");},
bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,34) eq "1" ? "ok" : "critical";},
# sync => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,35,35) eq "1" ? "true" : "false");},
}
} ,
51 =>
{
sensortype => 'Lidl Wetterstation 2759001/IAN114324',
model => 'SD_WS_51_TH',
prematch => sub {my $msg = shift; return 1 if ($msg =~ /^[0-9A-F]{10}/); }, # prematch
crcok => sub {return 1; }, # crc is unknown
id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,0,12); }, # random id?
# sendmode => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,10,11) eq "1" ? "manual" : "auto"; }
temp => sub {my (undef,$bitData) = @_; return round(((SD_WS_binaryToNumber($bitData,16,27)) -1220) *5 /90.0,1); }, #temp
hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,28,31)*10) + (SD_WS_binaryToNumber($bitData,32,35)); }, #hum
channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,36,39) ); }, #channel
bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,13) eq "1" ? "crititcal" : "ok";},
trend => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,15,16) eq "01" ? "rising" : SD_WS_binaryToNumber($bitData,14,15) eq "00" ? "neutral" : "rising";},
# sync => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,35,35) eq "1" ? "true" : "false");},
} ,
58 =>
{
sensortype => 'TFA 3032080',
model => 'SD_WS_58_TH',
prematch => sub {my $msg = shift; return 1 if ($msg =~ /^45[0-9A-F]{11}/); }, # prematch
crcok => sub { my $msg = shift;
my @buff = split(//,substr($msg,index($msg,"45"),10));
my $crc_check = substr($msg,index($msg,"45")+10,2);
my $mask = 0x7C;
my $checksum = 0x64;
my $data;
my $nibbleCount;
for ( $nibbleCount=0; $nibbleCount < scalar @buff; $nibbleCount+=2)
{
my $bitCnt;
if ($nibbleCount+1 <scalar @buff)
{
$data = hex($buff[$nibbleCount].$buff[$nibbleCount+1]);
} else {
$data = hex($buff[$nibbleCount]);
}
for ( my $bitCnt= 7; $bitCnt >= 0 ; $bitCnt-- )
{
my $bit;
# Rotate mask right
$bit = $mask & 1;
$mask = ($mask >> 1 ) | ($mask << 7) & 0xFF;
if ( $bit )
{
$mask ^= 0x18 & 0xFF;
}
# XOR mask into checksum if data bit is 1
if ( $data & 0x80 )
{
$checksum ^= $mask & 0xFF;
}
$data <<= 1 & 0xFF;
}
}
if ($checksum == hex($crc_check)) {
return 1;
} else {
return 0;
}
},
id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,8,15); }, # random id
bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,16) eq "1" ? "crititcal" : "ok";}, # bat?
channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,17,19)+1 ); }, # channel
temp => sub {my (undef,$bitData) = @_; return round((SD_WS_binaryToNumber($bitData,20,31)-720)*0.0556,1); }, # temp
hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,32,39)); }, # hum
} ,
);
Log3 $name, 4, "SD_WS_Parse: Protocol: $protocol, rawData: $rawData";
if ($protocol eq "37") # Bresser 7009994
{
# 0 7 8 9 10 12 22 25 31
# 01011010 0 0 01 01100001110 10 0111101 11001010
# ID B? T Kan Temp ?? Hum Pruefsumme?
# MU;P0=729;P1=-736;P2=483;P3=-251;P4=238;P5=-491;D=010101012323452323454523454545234523234545234523232345454545232345454545452323232345232340;CP=4;
if ($protocol eq "37") { # Bresser 7009994
# Protokollbeschreibung:
# https://github.com/merbanan/rtl_433_tests/tree/master/tests/bresser_3ch
# The data is grouped in 5 bytes / 10 nibbles
# ------------------------------------------------------------------------
# 0 | 8 12 | 16 | 24 | 32
# 1111 1100 | 0001 0110 | 0001 0000 | 0011 0111 | 0101 1001 0 65.1 F 55 %
# iiii iiii | bscc tttt | tttt tttt | hhhh hhhh | xxxx xxxx
# i: 8 bit random id (changes on power-loss)
# b: battery indicator (0=>OK, 1=>LOW)
# s: Test/Sync (0=>Normal, 1=>Test-Button pressed / Sync)
# c: Channel (MSB-first, valid channels are 1-3)
# t: Temperature (MSB-first, Big-endian)
# 12 bit unsigned fahrenheit offset by 90 and scaled by 10
# h: Humidity (MSB-first) 8 bit relative humidity percentage
# x: checksum (byte1 + byte2 + byte3 + byte4) % 256
# Check with e.g. (byte1 + byte2 + byte3 + byte4 - byte5) % 256) = 0
$model = "SD_WS37_TH";
$SensorTyp = "Bresser 7009994";
$id = SD_WS_binaryToNumber($bitData,0,7);
#$bat = int(substr($bitData,8,1)) eq "1" ? "ok" : "low";
$channel = SD_WS_binaryToNumber($bitData,10,11);
$rawTemp = SD_WS_binaryToNumber($bitData,12,22);
$hum = SD_WS_binaryToNumber($bitData,25,31);
$id = sprintf('%02X', $id); # wandeln nach hex
$temp = ($rawTemp - 609.93) / 9.014;
$temp = sprintf("%.1f", $temp);
if ($hum < 10 || $hum > 99 || $temp < -30 || $temp > 70) {
my $checksum = (SD_WS_binaryToNumber($bitData,0,7) + SD_WS_binaryToNumber($bitData,8,15) + SD_WS_binaryToNumber($bitData,16,23) + SD_WS_binaryToNumber($bitData,24,31)) & 0xFF;
if ($checksum != SD_WS_binaryToNumber($bitData,32,39)) {
Log3 $name, 3, "$name: SD_WS37 ERROR - checksum $checksum != ".SD_WS_binaryToNumber($bitData,32,39);
return "";
} else {
Log3 $name, 4, "$name: SD_WS37 checksum ok $checksum = ".SD_WS_binaryToNumber($bitData,32,39);
$id = SD_WS_binaryToNumber($bitData,0,7);
$id = sprintf('%02X', $id); # wandeln nach hex
$bat = int(substr($bitData,8,1)) eq "0" ? "ok" : "low"; # Batterie-Bit konnte nicht geprueft werden
$channel = SD_WS_binaryToNumber($bitData,10,11);
$rawTemp = SD_WS_binaryToNumber($bitData,12,23);
$hum = SD_WS_binaryToNumber($bitData,24,31);
my $tempFh = $rawTemp / 10 - 90; # Grad Fahrenheit
$temp = (($tempFh - 32) * 5 / 9); # Grad Celsius
$temp = sprintf("%.1f", $temp + 0.05); # round
Log3 $name, 4, "$name: SD_WS37 tempraw = $rawTemp, temp = $tempFh F, temp = $temp C, Hum = $hum";
Log3 $name, 4, "$name: SD_WS37 decoded protocol = $protocol ($SensorTyp), sensor id = $id, channel = $channel";
}
$bitData2 = substr($bitData,0,8) . ' ' . substr($bitData,8,4) . ' ' . substr($bitData,12,11);
$bitData2 = $bitData2 . ' ' . substr($bitData,23,2) . ' ' . substr($bitData,25,7) . ' ' . substr($bitData,32,8);
Log3 $iohash, 4, "$name converted to bits: " . $bitData2;
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, rawTemp=$rawTemp, temp=$temp, hum=$hum";
}
elsif ($protocol eq "44" || $protocol eq "44x") # BresserTemeo
{
# 0 4 8 12 20 24 28 32 36 40 44 52 56 60
# 0101 0111 1001 00010101 0010 0100 0001 1010 1000 0110 11101010 1101 1011 1110 110110010
# hhhh hhhh ?bcc iiiiiiii sttt tttt tttt xxxx xxxx ?BCC IIIIIIII Syyy yyyy yyyy
# hhhh hhhh ?bcc viiiiiii sttt tttt tttt xxxx xxxx ?BCC VIIIIIII Syyy yyyy yyyy
# - h humidity / -x checksum
# - t temp / -y checksum
# - c Channel / C checksum
# - i 8 bit random id (aendert sich beim Batterie- und Kanalwechsel) / - I checksum
# - c Channel / -C checksum
# - V sign / -V checksum
# - i 7 bit random id (aendert sich beim Batterie- und Kanalwechsel) / - I checksum
# - b battery indicator (0=>OK, 1=>LOW) / - B checksum
# - s Test/Sync (0=>Normal, 1=>Test-Button pressed) / - S checksum
@ -250,6 +347,22 @@ sub SD_WS_Parse($$)
return "";
}
my $sign = substr($binvalue,12,1);
my $checkSign = substr($binvalue,44,1) ^ 0b1;
if ($sign != $checkSign)
{
Log3 $iohash, 4, "SD_WS_Parse BresserTemeo: checksum error in Sign";
$checksumOkay = 0;
}
else
{
if ($sign)
{
$temp = 0 - $temp
}
}
$bat = substr($binvalue,9,1);
my $checkBat = substr($binvalue,41,1) ^ 0b1;
@ -265,8 +378,8 @@ sub SD_WS_Parse($$)
$channel = SD_WS_binaryToNumber($binvalue, 10, 11);
my $checkChannel = SD_WS_binaryToNumber($binvalue, 42, 43) ^ 0b11;
$id = SD_WS_binaryToNumber($binvalue, 12, 19);
my $checkId = SD_WS_binaryToNumber($binvalue, 44, 51) ^ 0b11111111;
$id = SD_WS_binaryToNumber($binvalue, 13, 19);
my $checkId = SD_WS_binaryToNumber($binvalue, 45, 51) ^ 0b1111111;
if ($channel != $checkChannel || $id != $checkId)
{
@ -283,15 +396,145 @@ sub SD_WS_Parse($$)
$id = sprintf('%02X', $id); # wandeln nach hex
Log3 $iohash, 4, "$name SD_WS_Parse: model=$model, temp=$temp, hum=$hum, channel=$channel, id=$id, bat=$bat";
}
} elsif ($protocol eq "64") # WH2
{
#* Fine Offset Electronics WH2 Temperature/Humidity sensor protocol
#* aka Agimex Rosenborg 66796 (sold in Denmark)
#* aka ClimeMET CM9088 (Sold in UK)
#* aka TFA Dostmann/Wertheim 30.3157 (Temperature only!) (sold in Germany)
#* aka ...
#*
#* The sensor sends two identical packages of 48 bits each ~48s. The bits are PWM modulated with On Off Keying
# * The data is grouped in 6 bytes / 12 nibbles
#* [pre] [pre] [type] [id] [id] [temp] [temp] [temp] [humi] [humi] [crc] [crc]
#*
#* pre is always 0xFF
#* type is always 0x4 (may be different for different sensor type?)
#* id is a random id that is generated when the sensor starts
#* temp is 12 bit signed magnitude scaled by 10 celcius
#* humi is 8 bit relative humidity percentage
#* Based on reverse engineering with gnu-radio and the nice article here:
#* http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/
# 0x4A/74 0x70/112 0xEF/239 0xFF/255 0x97/151 | Sensor ID: 0x4A7 | 255% | 239 | OK
#{ Dispatch($defs{sduino}, "W64#FF48D0C9FFBA", undef) }
#* Message Format:
#* .- [0] -. .- [1] -. .- [2] -. .- [3] -. .- [4] -.
#* | | | | | | | | | |
#* SSSS.DDDD DDN_.TTTT TTTT.TTTT WHHH.HHHH CCCC.CCCC
#* | | | || | | | | | | || | | |
#* | | | || | | | | | | || | `--------- CRC
#* | | | || | | | | | | |`-------- Humidity
#* | | | || | | | | | | |
#* | | | || | | | | | | `---- weak battery
#* | | | || | | | | | |
#* | | | || | | | | `----- Temperature T * 0.1
#* | | | || | | | |
#* | | | || | | `---------- Temperature T * 1
#* | | | || | |
#* | | | || `--------------- Temperature T * 10
#* | | | | `--- new battery
#* | | `---------- ID
#* `---- START = 9
#*
#*/
$msg = substr($msg,0,16);
my (undef ,$rawData) = split("#",$msg);
my $hlen = length($rawData);
my $blen = $hlen * 4;
my $msg_vor ="W64#";
my $bitData20;
my $sign = 0;
my $rr2;
my $vorpre = -1;
my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
my $temptyp = substr($bitData,0,8);
if( $temptyp == "11111110" ) {
$rawData = SD_WS_WH2SHIFT($rawData);
$msg = $msg_vor.$rawData;
$bitData = unpack("B$blen", pack("H$hlen", $rawData));
Log3 $iohash, 4, "$name: SD_WS_WH2_1 msg=$msg length:".length($bitData) ;
Log3 $iohash, 4, "$name: SD_WS_WH2_1 bitdata: $bitData" ;
} else{
if ( $temptyp == "11111101" ) {
$rawData = SD_WS_WH2SHIFT($rawData);
$rawData = SD_WS_WH2SHIFT($rawData);
$msg = $msg_vor.$rawData;
$bitData = unpack("B$blen", pack("H$hlen", $rawData));
Log3 $iohash, 4, "$name: SD_WS_WH2_2 msg=$msg length:".length($bitData) ;
Log3 $iohash, 4, "$name: SD_WS_WH2_2 bitdata: $bitData" ;
}
}
if( $temptyp == "11111111" ) {
$vorpre = 8;
}else{
Log3 $iohash, 4, "$name: SD_WS_WH2_4 Error kein WH2: Typ: $temptyp" ;
return "";
}
my $rc = eval
{
require Digest::CRC;
Digest::CRC->import();
1;
};
if($rc)
{
# Digest::CRC loaded and imported successfully
Log3 $iohash, 4, "$name: SD_WS_WH2_1 msg: $msg raw: $rawData " ;
$rr2 = SD_WS_WH2CRCCHECK($rawData);
if ($rr2 == 0 ){
# 1.CRC OK
Log3 $iohash, 4, "$name: SD_WS_WH2_1 CRC_OK : CRC=$rr2 msg: $msg check:".$rawData ;
}else{
Log3 $iohash, 4, "$name: SD_WS_WH2_4 CRC_Error: CRC=$rr2 msg: $msg check:".$rawData ;
return "";
}
}else {
Log3 $iohash, 1, "$name: SD_WS_WH2_3 CRC_not_load: Modul Digest::CRC fehlt" ;
return "";
}
$bitData = unpack("B$blen", pack("H$hlen", $rawData));
Log3 $iohash, 4, "$name converted to bits: WH2 " . $bitData;
$model = "SD_WS_WH2";
$SensorTyp = "WH2";
$id = SD_WS_bin2dec(substr($bitData,$vorpre + 4,6));
$id = sprintf('%03X', $id);
$channel = 0;
$bat = SD_WS_bin2dec(substr($bitData,$vorpre + 20,1));
$sign = SD_WS_bin2dec(substr($bitData,$vorpre + 12,1));
if ($sign == 0) {
# Temp positiv
$temp = (SD_WS_bin2dec(substr($bitData,$vorpre + 13,11))) / 10;
}else{
# Temp negativ
$temp = -(SD_WS_bin2dec(substr($bitData,$vorpre + 13,11))) / 10;
}
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, Data:".substr($bitData,$vorpre + 12,12)." temp=$temp";
$hum = SD_WS_bin2dec(substr($bitData,$vorpre + 24,8)); # TFA 30.3157 nur Temp, Hum = 255
Log3 $iohash, 4, "$name SD_WS_WH2_8: $protocol ($SensorTyp) sensor id=$id, Data:".substr($bitData,$vorpre + 24,8)." hum=$hum";
Log3 $iohash, 4, "$name SD_WS_WH2_9: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum";
}
elsif (defined($decodingSubs{$protocol})) # durch den hash decodieren
{
$SensorTyp=$decodingSubs{$protocol}{sensortype};
return "" && Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) prematch error" if (!$decodingSubs{$protocol}{prematch}->( $rawData ));
return "" && Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) crc error" if (!$decodingSubs{$protocol}{crcok}->( $rawData ));
if (!$decodingSubs{$protocol}{prematch}->( $rawData ))
{
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) prematch error" ;
return "";
}
my $retcrc=$decodingSubs{$protocol}{crcok}->( $rawData );
if (!$retcrc) {
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) crc error: $retcrc";
return "";
}
$id=$decodingSubs{$protocol}{id}->( $rawData,$bitData );
#my $temphex=$decodingSubs{$protocol}{temphex}->( $rawData,$bitData );
@ -299,13 +542,14 @@ sub SD_WS_Parse($$)
$hum=$decodingSubs{$protocol}{hum}->( $rawData,$bitData );
$channel=$decodingSubs{$protocol}{channel}->( $rawData,$bitData );
$model = $decodingSubs{$protocol}{model};
$bat = $decodingSubs{$protocol}{bat};
$bat = $decodingSubs{$protocol}{bat}->( $rawData,$bitData );
$trend = $decodingSubs{$protocol}{trend}->( $rawData,$bitData ) if (defined($decodingSubs{$protocol}{trend}));
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum";
Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat";
}
else {
Log3 $iohash, 4, "SD_WS_Parse: unknown message, please report. converted to bits: $bitData";
Log3 $iohash, 2, "SD_WS_WH2: unknown message, please report. converted to bits: $bitData";
return undef;
}
@ -334,8 +578,7 @@ sub SD_WS_Parse($$)
Log3 $iohash, 1, 'SD_WS: UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode;
return "UNDEFINED $deviceCode SD_WS $deviceCode";
}
#Log3 $iohash, 3, 'SD_WS: ' . $def->{NAME} . ' ' . $id;
my $hash = $def;
$name = $hash->{NAME};
return "" if(IsIgnored($name));
@ -358,16 +601,16 @@ sub SD_WS_Parse($$)
} else {
$hash->{bitMSG} = $bitData;
}
my $state = "T: $temp" . ($hum > 0 ? " H: $hum":"");
my $state = (($temp > -60 && $temp < 70) ? "T: $temp":"T: xx") . (($hum > 0 && $hum < 100) ? " H: $hum":"");
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $state);
readingsBulkUpdate($hash, "temperature", $temp) if (defined($temp));
readingsBulkUpdate($hash, "humidity", $hum) if (defined($hum) && $hum > 0);
readingsBulkUpdate($hash, "battery", $bat) if (defined($bat));
readingsBulkUpdate($hash, "channel", $channel) if (defined($channel));
readingsBulkUpdate($hash, "trend", $trend) if (defined($trend));
readingsBulkUpdate($hash, "temperature", $temp) if (defined($temp)&& ($temp > -60 && $temp < 70 ));
readingsBulkUpdate($hash, "humidity", $hum) if (defined($hum) && ($hum > 0 && $hum < 100 )) ;
readingsBulkUpdate($hash, "battery", $bat) if (defined($bat) && length($bat) > 0) ;
readingsBulkUpdate($hash, "channel", $channel) if (defined($channel)&& length($channel) > 0);
readingsBulkUpdate($hash, "trend", $trend) if (defined($trend) && length($trend) > 0);
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
@ -390,6 +633,13 @@ sub SD_WS_Attr(@)
return undef;
}
sub SD_WS_bin2dec($)
{
my $h = shift;
my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32)));
return sprintf("%d", $int);
}
sub SD_WS_binaryToNumber
{
@ -401,6 +651,31 @@ sub SD_WS_binaryToNumber
return oct("0b".substr($binstr,$fbit,($lbit-$fbit)+1));
}
sub SD_WS_WH2CRCCHECK($) {
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_WS_CRCCHECK", 4, "SD_WS_WH2CRCCHECK : raw:$rawData CRC=$rr3 " ;
return $rr3 ;
}
sub SD_WS_WH2SHIFT($){
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_WS_WH2SHIFT", 4, "SD_WS_WH2SHIFT_0 raw: $rawData length:".length($bitData) ;
Log3 "SD_WS_WH2SHIFT", 4, "SD_WS_WH2SHIFT_1 bitdata: $bitData" ;
return $rawData;
}
1;
=pod
@ -417,6 +692,9 @@ sub SD_WS_binaryToNumber
<ul>
<li>Bresser 7009994</li>
<li>Opus XT300</li>
<li>BresserTemeo</li>
<li>WH2 (TFA Dostmann/Wertheim 30.3157(Temperature only!) (sold in Germany), Agimex Rosenborg 66796 (sold in Denmark),ClimeMET CM9088 (Sold in UK)</li>
<li>PV-8644 infactory Poolthermometer</li>
</ul>
<br>
New received device are add in fhem with autocreate.
@ -470,6 +748,8 @@ sub SD_WS_binaryToNumber
<li>Bresser 7009994</li>
<li>Opus XT300</li>
<li>BresserTemeo</li>
<li>WH2 (TFA Dostmann/Wertheim 30.3157(Temperatur!) (Deutschland), Agimex Rosenborg 66796 (Denmark),ClimeMET CM9088 (UK)</li>
<li>PV-8644 infactory Poolthermometer</li>
</ul>
<br>
Neu empfangene Sensoren werden in FHEM per autocreate angelegt.

View File

@ -3,7 +3,7 @@
#
# The purpose of this module is to support serval eurochron
# weather sensors like eas8007 which use the same protocol
# Sidey79 & Ralf9 2015-2016
# Sidey79, Ralf9 2015-2017
#
package main;
@ -20,11 +20,11 @@ SD_WS07_Initialize($)
{
my ($hash) = @_;
$hash->{Match} = "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}"; ## pos 7 ist aktuell immer 0xF
$hash->{Match} = "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}(#R[A-F0-9][A-F0-9]){0,1}\$"; ## pos 7 ist aktuell immer 0xF
$hash->{DefFn} = "SD_WS07_Define";
$hash->{UndefFn} = "SD_WS07_Undef";
$hash->{ParseFn} = "SD_WS07_Parse";
$hash->{AttrFn} = "SD_WS07_Attr";
$hash->{AttrFn} = "SD_WS07_Attr";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " .
"$readingFnAttributes ";
$hash->{AutoCreate} =
@ -73,7 +73,11 @@ SD_WS07_Parse($$)
my ($iohash, $msg) = @_;
#my $rawData = substr($msg, 2);
my $name = $iohash->{NAME};
my (undef ,$rawData) = split("#",$msg);
my (undef ,$rawData, $rssi) = split("#",$msg);
if (defined($rssi)) {
$rssi = hex(substr($rssi,1));
$rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74));
}
#$protocol=~ s/^P(\d+)/$1/; # extract protocol
my $model = "SD_WS07";
@ -81,7 +85,12 @@ SD_WS07_Parse($$)
my $blen = $hlen * 4;
my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
Log3 $name, 4, "SD_WS07_Parse $model ($msg) length: $hlen";
if (defined($rssi)) {
Log3 $name, 4, "$name SD_WS07_Parse $model ($msg) length: $hlen RSSI = $rssi";
} else {
Log3 $name, 4, "$name SD_WS07_Parse $model ($msg) length: $hlen";
}
# 4 8 9 12 24 28 36
# 0011 0110 1 010 000100000010 1111 00111000 0000 eas8007
@ -110,12 +119,9 @@ SD_WS07_Parse($$)
$model=$model."_T";
} else {
$model=$model."_TH";
}
if ($hum > 100) {
return ''; # Eigentlich muesste sowas wie ein skip rein, damit ggf. spaeter noch weitre Sensoren dekodiert werden koennen.
if ($hum < 10 || $hum > 99) {
return '';
}
}
if ($temp > 700 && $temp < 3840) {
@ -125,7 +131,7 @@ SD_WS07_Parse($$)
}
$temp /= 10;
Log3 $iohash, 4, "$model decoded protocolid: 7 sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat";
Log3 $iohash, 4, "$name $model decoded protocolid: 7 sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat";
my $deviceCode;
@ -144,7 +150,7 @@ SD_WS07_Parse($$)
$def = $modules{SD_WS07}{defptr}{$deviceCode} if(!$def);
if(!$def) {
Log3 $iohash, 1, 'SD_WS07: UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode;
Log3 $iohash, 1, "$name SD_WS07: UNDEFINED sensor $model detected, code $deviceCode";
return "UNDEFINED $deviceCode SD_WS07 $deviceCode";
}
#Log3 $iohash, 3, 'SD_WS07: ' . $def->{NAME} . ' ' . $id;
@ -153,7 +159,7 @@ SD_WS07_Parse($$)
$name = $hash->{NAME};
return "" if(IsIgnored($name));
Log3 $name, 4, "SD_WS07: $name ($rawData)";
Log3 $name, 4, "$iohash->{NAME} SD_WS07: $name ($rawData)";
if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef)))
{
@ -179,6 +185,10 @@ SD_WS07_Parse($$)
readingsEndUpdate($hash, 1); # Notify is done by Dispatch
if(defined($rssi)) {
$hash->{RSSI} = $rssi;
}
return $name;
}
@ -208,7 +218,7 @@ sub SD_WS07_Attr(@)
=begin html
<a name="SD_WS07"></a>
<h3>Wether Sensors protocol #7</h3>
<h3>Weather Sensors protocol #7</h3>
<ul>
The SD_WS07 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.<br>
<br>
@ -218,12 +228,12 @@ sub SD_WS07_Attr(@)
<li>Technoline WS6750/TX70DTH</li>
</ul>
<br>
New received device are add in fhem with autocreate.
New received devices are added in FHEM with autocreate.
<br><br>
<a name="SD_WS07_Define"></a>
<b>Define</b>
<ul>The received devices created automatically.<br>
<ul>The received devices are created automatically.<br>
The ID of the defice is the cannel or, if the longid attribute is specified, it is a combination of channel and some random generated bits at powering the sensor and the channel.<br>
If you want to use more sensors, than channels available, you can use the longid option to differentiate them.
</ul>
@ -232,18 +242,17 @@ sub SD_WS07_Attr(@)
<b>Generated readings:</b>
<br>Some devices may not support all readings, so they will not be presented<br>
<ul>
<li>State (T: H:)</li>
<li>state (T: H:)</li>
<li>temperature (&deg;C)</li>
<li>humidity: (The humidity (1-100 if available)</li>
<li>humidity: (the humidity 1-100)</li>
<li>battery: (low or ok)</li>
<li>channel: (The Channelnumber (number if)</li>
<li>channel: (the channelnumberf)</li>
</ul>
<br>
<b>Attributes</b>
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#ignore">ignore</a></li>
<li><a href="#model">model</a> ()</li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>
@ -251,8 +260,8 @@ sub SD_WS07_Attr(@)
<a name="SD_WS07_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS07_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS07_Get"></a>
<b>Get</b> <ul>N/A</ul><br>
</ul>
@ -263,7 +272,7 @@ sub SD_WS07_Attr(@)
<a name="SD_WS07"></a>
<h3>SD_WS07</h3>
<ul>
Das SD_WS07 Module verarbeitet von einem IO Geraet (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.<br>
Das SD_WS07 Modul verarbeitet von einem IO Geraet (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.<br>
<br>
<b>Unterst&uumltzte Modelle:</b>
<ul>
@ -279,14 +288,14 @@ sub SD_WS07_Attr(@)
<a name="SD_WS07_Define"></a>
<b>Define</b>
<ul>Die empfangenen Sensoren werden automatisch angelegt.<br>
Die ID der angelgten Sensoren ist entweder der Kanal des Sensors, oder wenn das Attribut longid gesetzt ist, dann wird die ID aus dem Kanal und einer Reihe von Bits erzeugt, welche der Sensor beim Einschalten zufaellig vergibt.<br>
Die ID der angelegten Sensoren ist entweder der Kanal des Sensors, oder wenn das Attribut longid gesetzt ist, dann wird die ID aus dem Kanal und einer Reihe von Bits erzeugt, welche der Sensor beim Einschalten zufaellig vergibt.<br>
</ul>
<br>
<a name="SD_WS07 Events"></a>
<b>Generierte Readings:</b>
<ul>
<li>State (T: H:)</li>
<li>temperature (&deg;C)</li>
<li>state: (T: H:)</li>
<li>temperature: (&deg;C)</li>
<li>humidity: (Luftfeuchte (1-100)</li>
<li>battery: (low oder ok)</li>
<li>channel: (Der Sensor Kanal)</li>
@ -296,7 +305,6 @@ sub SD_WS07_Attr(@)
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#ignore">ignore</a></li>
<li><a href="#model">model</a> ()</li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
</ul>
@ -304,8 +312,9 @@ sub SD_WS07_Attr(@)
<a name="SD_WS071_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS07_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS07_Get"></a>
<b>Get</b> <ul>N/A</ul><br>
</ul>

View File

@ -1,33 +1,44 @@
##############################################
##############################################
# $Id$
#
# 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 Digest::CRC qw(crc);
#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#[A-Fa-f0-9]+"; ## pos 7 ist aktuell immer 0xF
$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->{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 " , FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} };
{ "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 verbose:5" , FILTER => "%NAME", GPLOT => "WH1080wind4:windSpeed/windGust,", autocreateThreshold => "2:180"} };
}
@ -48,6 +59,10 @@
$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;
}
@ -70,6 +85,8 @@
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));
@ -78,175 +95,216 @@
my $rain = 0;
my $deviceCode = 0;
my $model = "undef"; # 0xFFA -> WS0101/WH1080 alles andere -> CTW600
my $modelattr ;
my $modelid;
my $windSpeed = 0;
my $windguest =0;
my $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;
my $hum;
my $windDirection ;
my $windDirectionText;
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;
$modelattr = AttrVal($iohash->{NAME},'WS09_WSModel',0);
if ($modelattr eq '0'){
$modelattr = "undef";
}
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;
}
my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0);
Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 Model=$modelattr" ;
my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble
Log3 $iohash, 3, "$name: SD_WS09_Parse0 Bin=$bitData syncp=$syncpos length:".length($bitData) ;
Log3 $iohash, 4, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 " ;
$rawData_merk = $rawData;
my $rc = eval
{
require Digest::CRC;
Digest::CRC->import();
1;
};
if ($syncpos ==-1 || length($bitData)-$syncpos < 78)
{
Log3 $iohash, 3, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ;
return undef;
}
my $wh = substr($bitData,0,8);
#CRC-Check bei WH1080/WS0101 WS09_CRCAUS=0 und WS09_WSModel = undef oder Wh1080
if(($crcwh1080 == 0) && ($modelattr ne "CTW600")) {
if($wh == "11111111") {
if ($syncpos == 0)
{
$hlen = length($rawData);
$blen = $hlen * 4;
$bitData2 = '11'.unpack("B$blen", pack("H$hlen", $rawData));
$bitData20 = substr($bitData2,0,length($bitData2)-2);
$blen = length($bitData20);
$hlen = $blen / 4;
$msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20));
$bitData = $bitData20;
Log3 $iohash, 3, "$name: SD_WS09_Parse sync1 msg=$msg syncp=$syncpos length:".length($bitData) ;
}
if ($syncpos == 1)
{
$hlen = length($rawData);
$blen = $hlen * 4;
$bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData));
$bitData20 = substr($bitData2,0,length($bitData2)-1);
$blen = length($bitData20);
$hlen = $blen / 4;
$msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20));
$bitData = $bitData20;
Log3 $iohash, 3, "$name: SD_WS09_Parse sync2 msg=$msg syncp=$syncpos length:".length($bitData) ;
}
my $datacheck = pack( 'H*', substr($msg,5,length($msg)-5) );
my $crcmein = Digest::CRC->new(width => 8, poly => 0x31);
my $rr2 = $crcmein->add($datacheck)->hexdigest;
if ($rr2 eq "0"){
$model = "WH1080";
Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_OK: CRC=$rr2 Model=$model attr=$modelattr" ;
}else{
Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_Error: msg=$msg CRC=$rr2 " ;
return undef;
}
}else{
$model = "CTW600";
Log3 $iohash, 3, "$name: SD_WS09_Parse CTW600: Model=$model attr=$modelattr" ;
}
};
if( ($wh == "11111111") || ($model eq "WH1080")) {
if ($modelattr eq "CTW600"){
Log3 $iohash, 3, "$name: SD_WS09_WH1080 off=$modelattr Model=$model " ;
return undef;
}
$sensdata = substr($bitData,8);
my $whid = substr($sensdata,0,4);
if( $whid == "1010" ){ # A
Log3 $iohash, 3, "$name: SD_WS09_Parse WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ;
if($rc) # test ob Digest::CRC geladen wurde
{
$rr2 = SD_WS09_CRCCHECK($rawData);
if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) {
# 1. OK
$model = "WH1080";
Log3 $iohash, 4, "$name: SD_WS09_SHIFT_0 OK rwa:$rawData" ;
} 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)) ) {
# 2.ok
$msg = $msg_vor.$rawData;
$model = "WH1080";
$id = SD_WS09_bin2dec(substr($sensdata,4,8));
$bat = (SD_WS09_bin2dec((substr($sensdata,64,4))) == 0) ? 'ok':'low' ; # decode battery = 0 --> ok
$temp = (SD_WS09_bin2dec(substr($sensdata,12,12)) - 400)/10;
$hum = SD_WS09_bin2dec(substr($sensdata,24,8));
$windDirection = SD_WS09_bin2dec(substr($sensdata,68,4));
$windDirectionText = $winddir_name[$windDirection];
$windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01);
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
$windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
$rain = SD_WS09_bin2dec(substr($sensdata,56,8)) * 0.3;
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,56,8)." Dec: " . $rain ;
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 {
if( $whid == "1011" ){ # B DCF-77 Zeitmeldungen vom Sensor
my $hrs1 = substr($sensdata,16,8);
my $hrs;
my $mins;
my $sec;
my $mday;
my $month;
my $year;
$id = SD_WS09_bin2dec(substr($sensdata,4,8));
Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung0: HRS1=$hrs1 id:$id" ;
$hrs = SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde
$mins = SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute
$sec = SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde
#day month year
$year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr
$month = SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat
$mday = SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag
Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung1: msg=$rawData syncp=$syncpos length:".length($bitData) ;
Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung2: HH:mm:ss - ".$hrs.":".$mins.":".$sec ;
Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung3: dd.mm.yy - ".$mday.":".$month.":".$year ;
return $name;
}
Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung4: msg=$rawData syncp=$syncpos length:".length($sensdata) ;
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{
if ($modelattr eq "WH1080"){
Log3 $iohash, 3, "$name: SD_WS09_CTW600 off=$modelattr Model=$model " ;
return undef;
} else {
# eine CTW600 wurde erkannt
$sensdata = substr($bitData,$syncpos+8);
Log3 $iohash, 3, "$name: SD_WS09_Parse CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ;
$model = "CTW600";
my $nn1 = substr($sensdata,10,2); # Keine Bedeutung
my $nn2 = substr($sensdata,62,4); # Keine Bedeutung
$modelid = substr($sensdata,0,4);
Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." NN1:$nn1 NN2:$nn2" ;
Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." Bin-Sync=$sensdata syncp=$syncpos length:".length($sensdata) ;
$bat = SD_WS09_bin2dec((substr($sensdata,0,3))) ;
$id = SD_WS09_bin2dec(substr($sensdata,4,6));
$temp = (SD_WS09_bin2dec(substr($sensdata,12,10)) - 400)/10;
$hum = SD_WS09_bin2dec(substr($sensdata,22,8));
$windDirection = SD_WS09_bin2dec(substr($sensdata,66,4));
$windDirectionText = $winddir_name[$windDirection];
$windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01);
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
$windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
$rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01);
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ;
# 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, 3, "$name: SD_WS09_Parse ".$model." id:$id :$sensdata ";
Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain";
}
Log3 $iohash, 4, "$name: SD_WS09_Parse_19 ".$model." id:$id :$sensdata ";
if($hum > 100 || $hum < 0) {
Log3 $iohash, 3, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ;
Log3 $iohash, 4, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ;
return undef;
}
if($temp > 60 || $temp < -40) {
Log3 $iohash, 3, "$name: SD_WS09_Parse TEMP: Temp=$temp msg=$rawData " ;
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,/)))
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";
@ -261,58 +319,135 @@
Log3 $iohash, 1, 'SD_WS09_Parse UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode;
return "UNDEFINED $deviceCode SD_WS09 $deviceCode";
}
my $hash = $def;
$name = $hash->{NAME};
Log3 $name, 4, "SD_WS09_Parse: $name ($rawData)";
Log3 $name, 4, "SD_WS09_Parse_20: $name ($rawData)";
my $windkorr = AttrVal($hash->{NAME},'windKorrektur',0);
if ($windkorr != 0 )
{
my $oldwinddir = $windDirection;
$windDirection = $windDirection + $windkorr;
$windDirectionText = $winddir_name[$windDirection];
Log3 $iohash, 3, "SD_WS09_Parse ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ;
}
if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef)))
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 $deviceCode Dropped due to short time. minsecs=$minsecs";
return "";
Log3 $hash, 4, "SD_WS09_Parse_End $deviceCode Dropped due to short time. minsecs=$minsecs";
return undef;
}
}
$def->{lastMSG} = $rawData;
my $state = "T: $temp ". ($hum>0 ? " H: $hum ":" ")." Ws: $windSpeed "." Wg: $windguest "." Wd: $windDirectionText "." R: $rain";
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
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $state);
readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne"");
readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "" && $hum != 0 );
readingsBulkUpdate($hash, "battery", $bat) if ($bat ne "");
readingsBulkUpdate($hash, "id", $id) if ($id ne "");
$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" ;
#zusätzlich Daten für Wetterstation
readingsBulkUpdate($hash, "rain", $rain );
readingsBulkUpdate($hash, "windGust", $windguest );
readingsBulkUpdate($hash, "windSpeed", $windSpeed );
readingsBulkUpdate($hash, "windDirection", $windDirection );
readingsBulkUpdate($hash, "windDirectionDegree", $windDirection * 360 / 16);
readingsBulkUpdate($hash, "windDirectionText", $windDirectionText );
$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 "");
#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");
@ -323,7 +458,138 @@
$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);
# 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
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($)
{
@ -331,6 +597,7 @@
my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32)));
return sprintf("%d", $int);
}
sub SD_WS09_binflip($)
{
my $h = shift;
@ -341,11 +608,9 @@
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)));
@ -353,14 +618,38 @@
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;
=pod
=item summary Supports weather sensors protocl 9 from SIGNALduino
=item summary_DE Unterst&uumltzt Wettersensoren mit Protokol 9 vom SIGNALduino
=item summary Supports weather sensors (WH1080/3080/CTW-600) protocol 9 from SIGNALduino
=item summary_DE Unterstuetzt Wettersensoren (WH1080/3080/CTW-600) mit Protokol 9 vom SIGNALduino
=begin html
<a name="SD_WS09"></a>
@ -377,6 +666,7 @@
<li>WS-0101 --> Model: WH1080</li>
<li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
<li>1073 (WS1080) --> Model: WH1080</li>
<li>WH3080 --> Model: WH1080</li>
<li>CTW600 --> Model: CTW600 (??) </li>
</ul>
<br>
@ -399,8 +689,16 @@
<li>Humidity: (The humidity (1-100 if available)</li>
<li>Battery: (low or ok)</li>
<li>ID: (The ID-Number (number if)</li>
<li>windSpeed (m/s) and windDirection (N-O-S-W)</li>
<li>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>
<b>WH3080:</b>
<li>UV Index</li>
<li>Lux</li>
</ul>
<br>
<b>Attributes</b>
@ -409,19 +707,41 @@
<li><a href="#ignore">ignore</a></li>
<li><a href="#showtime">showtime</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
<li>Model<br>
WH1080, CTW600
<li>Model: WH1080,CTW600
</li>
<li>windKorrektur: -3,-2,-1,0,1,2,3
</li>
<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>
<li>windKorrektur<br>
-3,-2,-1,0,1,2,3
<li>WindDirAverageTime<br>
default is 600s, time span to be considered for the calculation
</li><br>
</ul>
<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>
<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>
<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>
<a name="SD_WS09_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS09_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<b>Parse</b> <ul>N/A</ul><br>
</ul>
@ -444,7 +764,8 @@
<li>WS-0101 --> Model: WH1080</li>
<li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
<li>1073 (WS1080) --> Model: WH1080</li>
<li>CTW600 --> Model: CTW600 (nicht getestet) </li>
<li>WH3080 --> Model: WH1080</li>
<li>CTW600 --> Model: CTW600</li>
</ul>
<br>
Neu empfangene Sensoren werden in FHEM per autocreate angelegt.
@ -460,13 +781,22 @@
<a name="SD_WS09 Events"></a>
<b>Generierte Readings:</b>
<ul>
<li>State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, windDirection, Rain</li>
<li>State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, Einheit, 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 (m/s) and windDirection (N-O-S-W)</li>
<li>windSpeed/windgust (Einheit siehe Unit_of_Wind) and windDirection (N-O-S-W)</li>
<li>Rain (mm)</li>
<li>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).</li>
<b>WH3080:</b>
<li>UV Index</li>
<li>Lux</li>
</ul>
<br>
<b>Attribute</b>
@ -482,15 +812,43 @@
Korrigiert die Nord-Ausrichtung des Windrichtungsmessers, wenn dieser nicht richtig nach Norden ausgerichtet ist.
-3,-2,-1,0,1,2,3
</li><br>
<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>
<li>WindDirAverageTime<br>
default ist 600s, Zeitspanne die für die Berechung berücksichtig werden soll
</li><br>
<li>WindDirAverageMinSpeed<br>
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
</li><br>
<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>
<li>WS09_CRCAUS<br>
Wird im Signalduino-Modul (00_SIGNALduino.pm) gesetzt
<br>0: CRC-Prüfung bei WH1080 CRC-Summe = 0
<br>2: CRC-Summe = 49 (x031) bei WH1080 wird als OK verarbeitet
</li><br>
</ul>
<a name="SD_WS09_Set"></a>
<b>Set</b> <ul>N/A</ul><br>
<a name="SD_WS09_Parse"></a>
<b>Set</b> <ul>N/A</ul><br>
<b>Parse</b> <ul>N/A</ul><br>
</ul>
=end html_DE
=cut