2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-21 14:04:15 +00:00

14_Hideki.pm: refactored code, fixed bug

git-svn-id: https://svn.fhem.de/fhem/trunk@27953 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
sidey79 2023-09-12 20:08:07 +00:00
parent 3f44ee0fab
commit d791459b9d
2 changed files with 441 additions and 383 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- bugifix: 14_Hideki.pm: winddirection fixed (#1099)
- change: 14_Hideki.pm: Update {Match} regex for Hideki Module (#1071)
- feature: SD_ProtocolData.pm: Protocol 122 for Temola TM 40 thermometer - feature: SD_ProtocolData.pm: Protocol 122 for Temola TM 40 thermometer
Protocol 119 Basic funkbus suppors Protocol 119 Basic funkbus suppors
Protocol 85 new sensor TFA 30.3251.10 Protocol 85 new sensor TFA 30.3251.10

View File

@ -7,34 +7,43 @@
# S. Butzek, HJGode, Ralf9 2015-2017 # S. Butzek, HJGode, Ralf9 2015-2017
# S. Butzek 2018-2022 # S. Butzek 2018-2022
# #
# changed the way crc and decrypt is used hjgode 20171129 # It is part of the SIGNALduinos project.
# https://github.com/RFD-FHEM/RFFHEM | see http://www.fhemwiki.de/wiki/SIGNALduino
#
# The module was modified by a few additions. support Hideki Sensors
# 2015-2017 S. Butzek, hjgode, Ralf9
# 2018- S. Butzek, elektron-bbs, HomeAutoUser, Ralf9
#
# 20171129 - hjgode, changed the way crc and decrypt is used
package main; package main;
#use version 0.77; our $VERSION = version->declare('v3.4.3'); #use version 0.77; our $VERSION = version->declare('v3.4.3');
use strict; use strict;
use warnings; use warnings;
use POSIX; use POSIX;
use FHEM::Meta; use FHEM::Meta;
eval {use Data::Dumper qw(Dumper);1};
#use Data::Dumper; #use Data::Dumper;
#####################################
sub
Hideki_Initialize($)
{
my ($hash) = @_;
#####################################
sub Hideki_Initialize {
my ($hash) = @_;
carp "Hideki_Initialize, hash failed" if (!$hash);
$hash->{Match} = qr/^P12#75[A-F0-9]{14,30}/; # Laenge (Anhahl nibbles nach 0x75 )noch genauer spezifizieren $hash->{Match} = qr/^P12#75[A-F0-9]{14,30}/; # Laenge (Anhahl nibbles nach 0x75 )noch genauer spezifizieren
$hash->{DefFn} = \&Hideki_Define; $hash->{DefFn} = \&Hideki_Define;
$hash->{UndefFn} = \&Hideki_Undef; $hash->{UndefFn} = \&Hideki_Undef;
$hash->{ParseFn} = \&Hideki_Parse; $hash->{ParseFn} = \&Hideki_Parse;
$hash->{AttrList} = "do_not_notify:0,1 showtime:0,1" $hash->{AttrList} = 'do_not_notify:0,1 showtime:0,1'
." ignore:0,1" .' ignore:0,1'
." windDirCorr windSpeedCorr" .' windDirCorr windSpeedCorr'
." $readingFnAttributes"; ." $readingFnAttributes";
$hash->{AutoCreate}= $hash->{AutoCreate}=
{ "Hideki.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} }; { "Hideki.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} };
@ -42,47 +51,119 @@ Hideki_Initialize($)
} }
##################################### my %comfortLevel = (
sub 0 => q[Hum. OK. Temp. uncomfortable (>24.9 or <20)],
Hideki_Define($$) 1 => q[Wet. More than 69% RHWet. More than 69% RH],
{ 2 => q[Dry. Less than 40% RH],
my ($hash, $def) = @_; 3 => q[Temp. and Hum. comfortable]
my @a = split("[ \t][ \t]*", $def); );
my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW");
my %allSensorTypes;
%allSensorTypes = (
30 => {
'temperature' => \&getTemperature,
'channel' => \&getChannel,
'battery' => \&getBattery,
'humidity' => \&getHumidity,
'comfort_level' => \&getComfort,
'package_number' => \&getCount,
'_eval' => {
'batteryState' => sub { return $_[0]->{battery} },
'state' => sub { return qq/T: $_[0]->{temperature} H: $_[0]->{humidity}/ }
}
},
31 => {
'temperature' => \&getTemperature,
'channel' => \&getChannel,
'battery' => \&getBattery,
'package_number' => \&getCount,
'_eval' => {
'batteryState' => sub { return $_[0]->{battery} },
'state' => sub { return qq/T: $_[0]->{temperature}/ }
}
},
14 => {
'rain' => \&getRain,
'channel' => \&getChannel,
'battery' => \&getBattery,
'package_number' => \&getCount,
'_eval' => {
'batteryState' => sub { return $_[0]->{battery} },
'state' => sub { return qq/R: $_[0]->{rain}/ }
}
},
12 => {
'temperature' => \&getTemperature,
'channel' => \&getChannel,
'battery' => \&getBattery,
'package_number' => \&getCount,
'windChill' => \&getWindchill,
'windDirection' => \&getWinddir,
'windDirectionDegree' => \&getWinddirdeg,
'windDirectionText' => \&getWinddirtext,
'windGust' => \&getWindgust,
'windSpeed' => \&getWindspeed,
'_eval' => {
'batteryState' => sub { return $_[0]->{battery} },
'_corrWindSpeed' => \&correctWindValues,
'state' => sub { return qq/T: $_[0]->{temperature} Ws: $_[0]->{windSpeed} Wg: $_[0]->{windGust} Wd: $_[0]->{windDirectionText}/ }
},
},
13 => {
'temperature' => \&getTemperature,
'channel' => \&getChannel,
'battery' => \&getBattery,
'package_number' => \&getCount,
'_eval' => {
'batteryState' => sub { return $_[0]->{battery} },
'state' => sub { return qq/T: $_[0]->{temperature}/ }
},
'debug' => sub { return q[type currently not full supported, please report sensor information] }
}
);
#####################################
sub Hideki_Define {
my ($hash, $def) = @_;
carp qq[Hideki_Define, too few arguments ($hash, $def)] if @_ < 2;
(ref $hash ne 'HASH') // return q[no hash provided];
my @a = split("[ \t][ \t]*", $def);
return "wrong syntax: define <name> Hideki <code>".int(@a) return "wrong syntax: define <name> Hideki <code>".int(@a)
if(int(@a) < 3); if(int(@a) < 3);
$hash->{CODE} = $a[2]; $hash->{CODE} = $a[2];
$hash->{lastMSG} = ""; $hash->{lastMSG} = '';
my $name= $hash->{NAME}; my $name= $hash->{NAME};
$modules{Hideki}{defptr}{$a[2]} = $hash; $modules{Hideki}{defptr}{$a[2]} = $hash;
#$hash->{STATE} = "Defined";
#AssignIoPort($hash); return;
return undef;
} }
##################################### #####################################
sub sub Hideki_Undef {
Hideki_Undef($$)
{
my ($hash, $name) = @_; my ($hash, $name) = @_;
carp qq[Hideki_Undef, too few arguments ($hash, $name)] if @_ < 2;
(ref $hash ne 'HASH') // return q[no hash provided];
delete($modules{Hideki}{defptr}{$hash->{CODE}}) if($hash && $hash->{CODE}); delete($modules{Hideki}{defptr}{$hash->{CODE}}) if($hash && $hash->{CODE});
return undef; return;
} }
##################################### #####################################
sub sub Hideki_Parse {
Hideki_Parse($$)
{
my ($iohash,$msg) = @_; my ($iohash,$msg) = @_;
my (undef ,$rawData) = split("#",$msg); carp qq[Hideki_Parse, too few arguments ($iohash, $msg)] if @_ < 2;
(ref $iohash ne 'HASH') // return q[no hash provided];
my (undef ,$rawData) = split(/#/,$msg);
my $ioname = $iohash->{NAME}; my $ioname = $iohash->{NAME};
my @a = split("", $msg); my @a = split(//, $msg);
Log3 $iohash, 4, "$ioname Hideki_Parse: incomming $msg"; Log3 $iohash, 4, "$ioname Hideki_Parse: incomming $msg";
my @decodedData; my @decodedData;
@ -103,159 +184,109 @@ Hideki_Parse($$)
} }
Log3 $iohash, 5, "$ioname Hideki_Parse: getSensorType for ".$decodedData[3]; Log3 $iohash, 5, "$ioname Hideki_Parse: getSensorType for ".$decodedData[3];
my $sensorTyp=($decodedData[3] & 0x1F); my $sensorTyp=getSensorType(\@decodedData);
Log3 $iohash, 4, "$ioname Hideki_Parse: SensorTyp = $sensorTyp decodedString = $decodedString"; Log3 $iohash, 4, "$ioname Hideki_Parse: SensorTyp = $sensorTyp decodedString = $decodedString";
my $id=substr($decodedString,2,2); # get the random id from the data my $id=substr($decodedString,2,2); # get the random id from the data
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 $deviceCode;
my $model= "Hideki_$sensorTyp"; my $model= qq[Hideki_$sensorTyp];
my $count=0;
my $comfort=0;
## 1. Detect what type of sensor we have, then call specific function to decode
if ($sensorTyp==30){
($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
$hum = 10 * ($decodedData[6] >> 4) + ($decodedData[6] & 0x0f);
$bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedData[3] >> 6; # verifiziert, MSG_Counter
$comfort = ($decodedData[7] >> 2 & 0x03); # comfort level
if ($comfort == 0) { $comfort = 'Hum. OK. Temp. uncomfortable (>24.9 or <20)' } ## 1. Detect what type of sensor we have, then call specific function to decode
elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' } if ( !exists $allSensorTypes{$sensorTyp} ) {
elsif ($comfort == 2) { $comfort = 'Dry. Less than 40% RH' } Log3 $iohash, 4, qq[$ioname Sensor type $sensorTyp not supported, please report sensor information!];
elsif ($comfort == 3) { $comfort = 'Temp. and Hum. comfortable' } #return q[];
$val = "T: $temp H: $hum"; };
Log3 $iohash, 4, "$ioname decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp, humidity=$hum, comfort=$comfort";
}elsif($sensorTyp==31){ # Build sensordecoder based on type
($channel, $temp) = decodeThermo(\@decodedData); my $sensorDecoder = $allSensorTypes{$sensorTyp};
$bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedData[3] >> 6; # verifiziert, MSG_Counter # Get values from decoder
$val = "T: $temp"; my %sensorData;
Log3 $iohash, 4, "$ioname decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp"; foreach my $key ( keys %{ $sensorDecoder } )
}elsif($sensorTyp==14){
($channel, $rain) = decodeRain(\@decodedData); # decodeThermoHygro($decodedString);
$bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "R: $rain";
Log3 $iohash, 4, "$ioname decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, rain=$rain, unknown=$unknown";
}elsif($sensorTyp==12){
($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
#($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedData); ## nach unten verschoben
$bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
#$val = "T: $temp Ws: $windspeed Wg: $windgust Wd: $winddirtext"; ## nach unten verschoben
Log3 $iohash, 4, "$ioname decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp";
}elsif($sensorTyp==13){
($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
$bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "T: $temp";
Log3 $iohash, 4, "$ioname decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp";
Log3 $iohash, 4, "$ioname Sensor Typ $sensorTyp currently not full supported, please report sensor information!";
}
else{
Log3 $iohash, 4, "$ioname Sensor Typ $sensorTyp not supported, please report sensor information!";
return "";
}
my $longids = AttrVal($iohash->{NAME},'longids',0);
if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/)))
{ {
$deviceCode=$model . "_" . $id . "." . $channel; next if (ref $sensorDecoder->{$key} ne q[CODE]);
$sensorData{$key} = $sensorDecoder->{$key}->(\@decodedData);
}
# Log received values
my $logstr = q{};
while( my ($key, $value) = each(%sensorData) ) {
next if ($key =~ /^_/x );
$logstr .= qq[, $key=$value];
}
Log3 $iohash, 4, qq[$ioname decoder Hideki protocol model=$model, sensor id=$id].$logstr;
# Get devicecode
my $longids = AttrVal($iohash->{NAME},'longids',0);
if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/x)))
{
$deviceCode=$model . "_" . $id . "." . $sensorData{channel};
Log3 $iohash,4, "$ioname Hideki_Parse: using longid: $longids model: $model"; Log3 $iohash,4, "$ioname Hideki_Parse: using longid: $longids model: $model";
} else { } else {
$deviceCode = $model . "_" . $channel; $deviceCode = $model . "_" . $sensorData{channel};
} }
Log3 $iohash, 5, "$ioname Hideki_Parse deviceCode: $deviceCode"; Log3 $iohash, 5, "$ioname Hideki_Parse deviceCode: $deviceCode";
# Check if device is defined
my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode}; my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode};
$def = $modules{Hideki}{defptr}{$deviceCode} if(!$def); $def = $modules{Hideki}{defptr}{$deviceCode} if(!$def);
if(!$def) { if(!$def) {
Log3 $iohash, 1, "$ioname Hideki: UNDEFINED sensor $deviceCode detected, code $msg"; Log3 $iohash, 1, "$ioname Hideki: UNDEFINED sensor $deviceCode detected, code $msg";
return "UNDEFINED $deviceCode Hideki $deviceCode"; return "UNDEFINED $deviceCode Hideki $deviceCode";
} }
# Check if device will receive update
my $hash = $def; my $hash = $def;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
return "" if(IsIgnored($name)); return "" if(IsIgnored($name));
$sensorData{_NAME} = $hash->{NAME};
#Log3 $name, 4, "Hideki: $name ($msg)";
if ($sensorTyp == 12) { # Wind
($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind($name, \@decodedData);
$val = "T: $temp Ws: $windspeed Wg: $windgust Wd: $winddirtext";
Log3 $name, 4, "$ioname $name Parse: model=12(wind), T: $temp, Wc=$windchill, Ws=$windspeed, Wg=$windgust, Wd=$winddir, WdDeg=$winddirdeg, Wdtxt=$winddirtext";
}
if (!defined(AttrVal($name,"event-min-interval",undef))) if (!defined(AttrVal($name,"event-min-interval",undef)))
{ {
my $minsecs = AttrVal($ioname,'minsecs',0); my $minsecs = AttrVal($ioname,'minsecs',0);
if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) { if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) {
Log3 $name, 4, "$name Hideki_Parse: $deviceCode Dropped ($decodedString) due to short time. minsecs=$minsecs"; Log3 $name, 4, "$name Hideki_Parse: $deviceCode Dropped ($decodedString) due to short time. minsecs=$minsecs";
return ""; return '';
} }
} }
# Update existing device
$hash->{lastReceive} = time(); $hash->{lastReceive} = time();
$def->{lastMSG} = $decodedString; $def->{lastMSG} = $decodedString;
#Log3 $name, 4, "Hideki update $name:". $name;
# Do some late evaluations bevore update readings
foreach my $key (sort keys %{ $sensorDecoder->{_eval} }) {
$sensorData{$key} = $sensorDecoder->{_eval}{$key}->(\%sensorData);
}
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "state", $val); while ( my ($key, $value) = each(%sensorData) ) {
readingsBulkUpdate($hash, "battery", $bat) if ($bat ne ""); next if ($key =~ /^[_\.]/x );
readingsBulkUpdate($hash, "batteryState", $bat) if ($bat ne ""); readingsBulkUpdate($hash,$key,$value);
readingsBulkUpdate($hash, "channel", $channel) if ($channel 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 == 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 readingsEndUpdate($hash, 1); # Notify is done by Dispatch
return $name; return $name;
} }
#####################################
# decryptAndCheck # decryptAndCheck
# input is raw data (array of bytes) # input is raw data (array of bytes)
# output is true if check1 and check2 OK # output is true if check1 and check2 OK
# data will then hold the decrypted data # data will then hold the decrypted data
sub decryptAndCheck { sub decryptAndCheck {
carp qq[decryptAndCheck, too few arguments (iohash, rawData)] if @_ < 2;
my $iohash = shift; my $iohash = shift;
my $rawData = shift; my $rawData = shift;
my $name = $iohash->{NAME}; my $name = $iohash->{NAME};
my $cs1=0; #will be zero for xor over all (bytes[2]>>1)&0x1F except first byte (always 0x75) my $cs1=0; #will be zero for xor over all (bytes[2]>>1)&0x1F except first byte (always 0x75)
my $cs2=0; my $cs2=0;
my $i; my $i;
my @data; my @data;
@data=map { hex($_) } ($rawData =~ /(..)/g); #byte array from raw hex data string @data=map { hex($_) } ($rawData =~ /(..)/gx); #byte array from raw hex data string
#/* Decrypt raw received data byte */ BYTE DecryptByte(BYTE b) { return b ^ (b << 1); } #/* Decrypt raw received data byte */ BYTE DecryptByte(BYTE b) { return b ^ (b << 1); }
my $count=( ($data[2] ^ ($data[2]<<1)) >>1 ) & 0x1f; my $count=( ($data[2] ^ ($data[2]<<1)) >>1 ) & 0x1f;
@ -266,13 +297,13 @@ sub decryptAndCheck {
} }
if($data[0] != 0x75) { if($data[0] != 0x75) {
Log3 $iohash, 4, "$name Hideki_Parse: rawData=$rawData is no Hideki"; Log3 $iohash, 4, "$name Hideki_Parse: rawData=$rawData is not Hideki";
return (0,@data); return (0,@data);
} }
#iterate over data only, first byte is 0x75 always #iterate over data only, first byte is 0x75 always
# read bytes 1 to n-2 , just before checksum # read bytes 1 to n-2 , just before checksum
for ($i=1; $i<($count+2); $i++) { for my $i (1..$count+1) {
$cs1 ^= $data[$i]; # calc first chksum $cs1 ^= $data[$i]; # calc first chksum
$cs2 = Hideki_SecondCheck($data[$i] ^ $cs2); $cs2 = Hideki_SecondCheck($data[$i] ^ $cs2);
$data[$i] ^= (($data[$i] << 1) & 0xFF); # decrypt byte at $i without overflow $data[$i] ^= (($data[$i] << 1) & 0xFF); # decrypt byte at $i without overflow
@ -288,9 +319,12 @@ sub decryptAndCheck {
return (1, @data); return (1, @data);
} }
#####################################
# /* The second checksum. Input is OldChecksum^NewByte */ # /* The second checksum. Input is OldChecksum^NewByte */
sub Hideki_SecondCheck { sub Hideki_SecondCheck {
carp qq[Hideki_SecondCheck, too few arguments] if @_ < 1;
my $b = shift; my $b = shift;
my $c = 0; my $c = 0;
if (($b & 0x80) == 0x80){ if (($b & 0x80) == 0x80){
$b^=0x95; $b^=0x95;
@ -306,6 +340,7 @@ sub Hideki_SecondCheck{
} }
#####################################
# return decoded sensor type # return decoded sensor type
# in: one byte # in: one byte
# out: one byte # out: one byte
@ -317,127 +352,147 @@ sub Hideki_SecondCheck{
# 0x1E Thermo/hygro-sensor # 0x1E Thermo/hygro-sensor
# 0x1F Thermo sensor # 0x1F Thermo sensor
sub getSensorType { sub getSensorType {
return ($_[0] & 0x1F); my $decodedData= shift // carp q[no bytes specified];
return $decodedData->[3] & 0x1F;
} }
# decode byte array and return channel, temperature #####################################
# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); # getters for serval values from the decrypted hexdata
# output <return code>, <channel>, <temperature> # input:hashref of hex value of received message
# was unable to get this working with an array ref as input, so switched to hex string input # output specific value
sub decodeThermo {
my @Hidekibytes = @{$_[0]};
#my $Hidekihex = shift; sub getTemperature {
#my @Hidekibytes=(); my $decodedData= shift // carp q[no bytes specified];
#for (my $i=0; $i<(length($Hidekihex))/2; $i++){ my $temp = 100 * ($decodedData->[5] & 0x0f) + 10 * ($decodedData->[4] >> 4) + ($decodedData->[4] & 0x0f);
# my $hex=hex(substr($Hidekihex, $i*2, 2)); ## Mit split und map geht es auch ... $str =~ /(..?)/g;
# push (@Hidekibytes, $hex);
#}
my $channel=0;
my $temp=0;
$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.
if ($channel >= 5) {
$channel--;
}
my $sensorId = $Hidekibytes[1] & 0x1f; # Extract random id from sensor
#my $devicetype = $Hidekibytes[3]&0x1f;
$temp = 100 * ($Hidekibytes[5] & 0x0f) + 10 * ($Hidekibytes[4] >> 4) + ($Hidekibytes[4] & 0x0f);
## // temp is negative? ## // temp is negative?
if (!($Hidekibytes[5] & 0x80)) { if (!($decodedData->[5] & 0x80)) { $temp = -$temp; }
$temp = -$temp;
return $temp = $temp / 10;
} }
$temp = $temp / 10; sub getChannel {
return ($channel, $temp); my $decodedData = shift // carp q[no bytes specified];
my $channel = $decodedData->[1] >> 5;
if ( $channel >= 5 ) { $channel--; }
return $channel
} }
sub getHumidity {
my $decodedData = shift // carp q[no bytes specified];
# decode byte array and return channel and total rain in mm return 10 * ($decodedData->[6] >> 4) + ($decodedData->[6] & 0x0f);
# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array);
# output <return code>, <channel>, <totalrain>
# was unable to get this working with an array ref as input, so switched to hex string input
sub decodeRain {
my @Hidekibytes = @{$_[0]};
#my $Hidekihex = shift;
#my @Hidekibytes=();
#for (my $i=0; $i<(length($Hidekihex))/2; $i++){
# my $hex=hex(substr($Hidekihex, $i*2, 2)); ## Mit split und map geht es auch ... $str =~ /(..?)/g;
# push (@Hidekibytes, $hex);
#}
my $channel=0;
my $rain=0;
my $unknown;
#my $tests=0;
#additional checks?
#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.
if ($channel >= 5) {
$channel--;
}
my $sensorId = $Hidekibytes[1] & 0x1f; # Extract random id from sensor
$rain = ($Hidekibytes[4] + $Hidekibytes[5]*0xff)*0.7;
return ($channel, $rain, $unknown);
} }
# P12#758BB244074007400F00001C6E7A01 sub getBattery {
sub wind { my $decodedData = shift // carp q[no bytes specified];
my $name = shift;
my @Hidekibytes = @{$_[0]};
my @wd=(0, 15, 13, 14, 9, 10, 12, 11, 1, 2, 4, 3, 8, 7, 5, 6);
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); return ($decodedData->[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
}
sub getCount {
my $decodedData = shift // carp q[no bytes specified];
return $decodedData->[3] >> 6; # verifiziert, MSG_Counter
}
sub getComfort {
my $decodedData = shift // carp q[no bytes specified];
my $comfortVal = ($decodedData->[7] >> 2 & 0x03); # comfort level
if ( !exists $comfortLevel{$comfortVal} ) { return $comfortVal; };
return $comfortLevel{$comfortVal};
}
sub getRain {
my $decodedData = shift // carp q[no bytes specified];
return ($decodedData->[4] + $decodedData->[5]*0xff)*0.7;
}
sub getWindchill {
my $decodedData = shift // carp q[no bytes specified];
my $windchill = 100 * ($decodedData->[7] & 0x0f) + 10 * ($decodedData->[6] >> 4) + ($decodedData->[6] & 0x0f);
## windchill is negative? ## windchill is negative?
if (!($Hidekibytes[7] & 0x80)) { if (!($decodedData->[7] & 0x80)) {
$windchill = -$windchill; $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);
my $windSpeedCorr = AttrVal($name,'windSpeedCorr',1); ### -> hierher verschoben
if ($windSpeedCorr > 0) {
$windspeed = sprintf("%.2f", $windspeed * $windSpeedCorr);
$windgust = sprintf("%.2f", $windgust * $windSpeedCorr);
Log3 $name, 5, "$name Hideki_Parse: WindSpeedCorr factor=$windSpeedCorr";
}
$winddir = $wd[$Hidekibytes[11] >> 4];
my $windDirCorr = AttrVal($name,'windDirCorr',0);
if ($windDirCorr > 0) {
$winddir += $windDirCorr;
$winddir &= 15;
Log3 $name, 5, "$name Hideki_Parse: windDirCorr=$windDirCorr";
}
$winddirtext = $winddir_name[$winddir];
$winddirdeg = $winddir * 22.5;
return ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext); return $windchill / 10;
} }
sub getWindspeed {
my $decodedData = shift // carp q[no bytes specified];
my $windspeed = ($decodedData->[9] & 0x0f ) * 100 + ($decodedData->[8] >> 4) * 10 + ($decodedData->[8] & 0x0f);
return sprintf("%.2f", $windspeed);
}
sub getWindgust {
my $decodedData = shift // carp q[no bytes specified];
my $windgust = ($decodedData->[10] >> 4) * 100 + ($decodedData->[10] & 0x0f) * 10 + ($decodedData->[9] >> 4);
return sprintf("%.2f", $windgust);
}
sub getWinddir {
my $decodedData = shift // carp q[no bytes specified];
my @wd=(0, 15, 13, 14, 9, 10, 12, 11, 1, 2, 4, 3, 8, 7, 5, 6);
return $wd[$decodedData->[11] >> 4];
}
sub getWinddirtext {
my $decodedData = shift // carp q[no bytes specified];
return $winddir_name[getWinddir($decodedData)];
}
sub getWinddirdeg {
my $decodedData = shift // carp q[no bytes specified];
return getWinddir($decodedData) * 22.5;
}
#####################################
# correct wind values if correction attributes are set
# input: hashref with prepared values from sensors
# output undef
sub correctWindValues {
my $sensorValues = shift // carp q[no values from sensor specified];
if (! IsDevice($sensorValues->{_NAME}) ) { carp q[no sensorname provided]; }
my $windSpeedCorr = AttrVal($sensorValues->{_NAME},'windSpeedCorr',1);
my $windDirCorr = AttrVal($sensorValues->{_NAME},'windDirCorr',0);
if ($windSpeedCorr > 0) {
$sensorValues->{windSpeed} = sprintf q[%.2f], $sensorValues->{windSpeed} * $windSpeedCorr ;
$sensorValues->{windGust} = sprintf q[%.2f], $sensorValues->{windGust} * $windSpeedCorr ;
Log3 $sensorValues->{_NAME}, 5, qq[$sensorValues->{_NAME} correctWindValues: WindSpeedCorr factor=$windSpeedCorr];
}
if ($windDirCorr > 0) {
$sensorValues->{windDirection} += $windDirCorr;
$sensorValues->{windDirection} &= 15;
$sensorValues->{windDirectionText} = $winddir_name[$sensorValues->{windDirection}];
$sensorValues->{windDirectionDegree} = $sensorValues->{windDirection} * 22.5;
Log3 $sensorValues->{_NAME}, 5, qq[$sensorValues->{_NAME} correctWindValues: windDirCorr=$windDirCorr];
}
return;
}
1; 1;
@ -641,7 +696,8 @@ sub wind {
}, },
"develop": { "develop": {
"requires": { "requires": {
"POSIX": "0" "POSIX": "0",
"Data::Dumper": 0
} }
} }
}, },