2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

14_Hideki.pm: changed decrypting messages and crc check

git-svn-id: https://svn.fhem.de/fhem/trunk@16509 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
sidey79 2018-03-29 19:41:57 +00:00
parent a09bf2c735
commit d99cfd2626
2 changed files with 103 additions and 95 deletions

View File

@ -1,5 +1,6 @@
# 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.
- feature: 14_Hideki: changed decrypting and improved crc check
- change: 14_SD_WS_Maverick: reworked module, renamed readings - change: 14_SD_WS_Maverick: reworked module, renamed readings
added commandref added commandref
- bugfix: 14_SD_WS07: Error retrieving rssi value fixed - bugfix: 14_SD_WS07: Error retrieving rssi value fixed

View File

@ -6,6 +6,7 @@
# to support Hideki Sensors # to support Hideki Sensors
# S. Butzek, HJGode, Ralf9 2015-2017 # S. Butzek, HJGode, Ralf9 2015-2017
# #
# changed the way crc and decrypt is used hjgode 20171129
package main; package main;
@ -79,31 +80,31 @@ Hideki_Parse($$)
my $name = $iohash->{NAME}; my $name = $iohash->{NAME};
my @a = split("", $msg); my @a = split("", $msg);
Log3 $iohash, 4, "Hideki_Parse $name incomming $msg"; Log3 $iohash, 4, "$name Hideki_Parse: incomming $msg";
# decrypt bytes my @decodedData;
my $decodedString = decryptBytes($rawData); # decrpyt hex string to hex string my $crc1crc2OK = 0;
($crc1crc2OK, @decodedData) = decryptAndCheck($iohash, $rawData); # use unencrypted rawdata
if ($crc1crc2OK == 0) {
return ''; #crc1 or crc2 failed
}
#convert dectypted hex str back to array of bytes: # decrypt and decodedBytes are now done with decryptAndCheck
my @decodedBytes = map { hex($_) } ($decodedString =~ /(..)/g); my $decodedString = join '', unpack('H*', pack('C*',@decodedData)); # get hex string
Log3 $iohash, 4, "$name Hideki_Parse: raw=$rawData, decoded=$decodedString";
if (!@decodedBytes)
{ if (!@decodedData) {
Log3 $iohash, 4, "$name decrypt failed"; Log3 $iohash, 4, "$name Hideki_Parse: decrypt failed";
return ''; return '';
} }
my $sensorTyp=getSensorType($decodedBytes[3]); Log3 $iohash, 5, "$name Hideki_Parse: getSensorType for ".$decodedData[3];
Log3 $iohash, 4, "Hideki_Parse SensorTyp = $sensorTyp decodedString = $decodedString"; my $sensorTyp=($decodedData[3] & 0x1F);
Log3 $iohash, 4, "$name Hideki_Parse: SensorTyp = $sensorTyp decodedString = $decodedString";
if (!Hideki_crc(\@decodedBytes))
{
Log3 $iohash, 4, "$name crc failed";
return '';
}
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 $channel=0;
my $temp=""; my $temp="";
my $hum=0; my $hum=0;
my $rain=0; my $rain=0;
@ -123,11 +124,11 @@ Hideki_Parse($$)
my $comfort=0; my $comfort=0;
## 1. Detect what type of sensor we have, then call specific function to decode ## 1. Detect what type of sensor we have, then call specific function to decode
if ($sensorTyp==30){ if ($sensorTyp==30){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); ($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
$hum = 10 * ($decodedBytes[6] >> 4) + ($decodedBytes[6] & 0x0f); $hum = 10 * ($decodedData[6] >> 4) + ($decodedData[6] & 0x0f);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter $count = $decodedData[3] >> 6; # verifiziert, MSG_Counter
$comfort = ($decodedBytes[7] >> 2 & 0x03); # comfort level $comfort = ($decodedData[7] >> 2 & 0x03); # comfort level
if ($comfort == 0) { $comfort = 'Hum. OK. Temp. uncomfortable (>24.9 or <20)' } if ($comfort == 0) { $comfort = 'Hum. OK. Temp. uncomfortable (>24.9 or <20)' }
elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' } elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' }
@ -136,28 +137,28 @@ Hideki_Parse($$)
$val = "T: $temp H: $hum"; $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"; 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){ }elsif($sensorTyp==31){
($channel, $temp) = decodeThermo(\@decodedBytes); ($channel, $temp) = decodeThermo(\@decodedData);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter $count = $decodedData[3] >> 6; # verifiziert, MSG_Counter
$val = "T: $temp"; $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 decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp";
}elsif($sensorTyp==14){ }elsif($sensorTyp==14){
($channel, $rain) = decodeRain(\@decodedBytes); # decodeThermoHygro($decodedString); ($channel, $rain) = decodeRain(\@decodedData); # decodeThermoHygro($decodedString);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter $count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "R: $rain"; $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"; 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){ }elsif($sensorTyp==12){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); ($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedBytes); ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedData);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter $count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "T: $temp Ws: $windspeed Wg: $windgust Wd: $winddirtext"; $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"; 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){ }elsif($sensorTyp==13){
($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); ($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString);
$bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery
$count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter $count = $decodedData[3] >> 6; # UNVERIFIZIERT, MSG_Counter
$val = "T: $temp"; $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 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!"; Log3 $iohash, 4, "$name Sensor Typ $sensorTyp currently not full supported, please report sensor information!";
@ -170,18 +171,18 @@ Hideki_Parse($$)
if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/)))
{ {
$deviceCode=$model . "_" . $id . "." . $channel; $deviceCode=$model . "_" . $id . "." . $channel;
Log3 $iohash,4, "$name using longid: $longids model: $model"; Log3 $iohash,4, "$name Hideki_Parse: using longid: $longids model: $model";
} else { } else {
$deviceCode = $model . "_" . $channel; $deviceCode = $model . "_" . $channel;
} }
Log3 $iohash, 5, "deviceCode: $deviceCode"; Log3 $iohash, 5, "$name Hideki_Parse deviceCode: $deviceCode";
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, "$name Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode"; Log3 $iohash, 1, "$name Hideki: UNDEFINED sensor $deviceCode detected, code $msg";
return "UNDEFINED $deviceCode Hideki $deviceCode"; return "UNDEFINED $deviceCode Hideki $deviceCode";
} }
@ -202,7 +203,7 @@ Hideki_Parse($$)
{ {
my $minsecs = AttrVal($iohash->{NAME},'minsecs',0); my $minsecs = AttrVal($iohash->{NAME},'minsecs',0);
if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) { if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) {
Log3 $iohash, 4, "$deviceCode Dropped ($decodedString) due to short time. minsecs=$minsecs"; Log3 $iohash, 4, "$name Hideki_Parse: $deviceCode Dropped ($decodedString) due to short time. minsecs=$minsecs";
return ""; return "";
} }
} }
@ -239,41 +240,69 @@ Hideki_Parse($$)
return $name; return $name;
} }
# check crc for incoming message # decryptAndCheck
# in: hex string with encrypted, raw data, starting with 75 # input is raw data (array of bytes)
# out: 1 for OK, 0 for failed # output is true if check1 and check2 OK
# sample "75BDBA4AC2BEC855AC0A00" # data will then hold the decrypted data
sub Hideki_crc{ sub decryptAndCheck {
#my $Hidekihex=shift; my $iohash = shift;
#my @Hidekibytes=shift; my $rawData = shift;
my $name = $iohash->{NAME};
my @Hidekibytes = @{$_[0]}; my $cs1=0; #will be zero for xor over all (bytes[2]>>1)&0x1F except first byte (always 0x75)
#push @Hidekibytes,0x75; #first byte always 75 and will not be included in decrypt/encrypt! my $cs2=0;
#convert to array except for first hex my $i;
#for (my $i=1; $i<(length($Hidekihex))/2; $i++){ my @data;
# my $hex=Hideki_decryptByte(hex(substr($Hidekihex, $i*2, 2))); @data=map { hex($_) } ($rawData =~ /(..)/g); #byte array from raw hex data string
# push (@Hidekibytes, $hex);
#} #/* Decrypt raw received data byte */ BYTE DecryptByte(BYTE b) { return b ^ (b << 1); }
my $count=( ($data[2] ^ ($data[2]<<1)) >>1 ) & 0x1f;
my $cs1=0; #will be zero for xor over all (bytes>>1)&0x1F except first byte (always 0x75) my $L = scalar @data;
#my $rawData=shift; if ($L <= $count+2) {
#todo add the crc check here Log3 $iohash, 4, "$name Hideki_crc: rawdata=$rawData to short, count=$count data length=$L";
return (0,@data);
my $count=($Hidekibytes[2]>>1) & 0x1f; }
my $b;
if($data[0] != 0x75) {
Log3 $iohash, 4, "$name Hideki_Parse: rawData=$rawData is no Hideki";
return (0,@data);
}
#iterate over data only, first byte is 0x75 always #iterate over data only, first byte is 0x75 always
for (my $i=1; $i<$count+2 && $i<scalar @Hidekibytes; $i++) { # read bytes 1 to n-2 , just before checksum
$b = $Hidekibytes[$i]; for ($i=1; $i<($count+2); $i++) {
$cs1 = $cs1 ^ $b; # calc first chksum $cs1 ^= $data[$i]; # calc first chksum
$cs2 = Hideki_SecondCheck($data[$i] ^ $cs2);
$data[$i] ^= (($data[$i] << 1) & 0xFF); # decrypt byte at $i without overflow
} }
if($cs1==0){
return 1; $count += 2;
} if ($cs1 != 0 || $cs2 != $data[$count]) {
else{ Log3 $iohash, 4, "$name Hideki crcCheck FAILED: cs1 / cs2/checksum2 $cs1 / $cs2/$data[$count], rawData=$rawData, count+2=$count, length=$L";
return 0; return (0, @data);
} else {
Log3 $iohash, 4, "$name Hideki crcCheck ok: cs1/cs2 $cs1/$cs2, rawData=$rawData, count+2=$count, length=$L";
} }
return (1, @data);
} }
# /* The second checksum. Input is OldChecksum^NewByte */
sub Hideki_SecondCheck{
my $b = shift;
my $c = 0;
if (($b & 0x80) == 0x80){
$b^=0x95;
}
$c = $b^($b>>1);
if (($b & 1) == 1){
$c^=0x5f;
}
if (($c & 1) == 1){
$b^=0x5f;
}
return ($b^($c>>1));
}
# return decoded sensor type # return decoded sensor type
# in: one byte # in: one byte
# out: one byte # out: one byte
@ -285,31 +314,9 @@ sub Hideki_crc{
# 0x1E Thermo/hygro-sensor # 0x1E Thermo/hygro-sensor
# 0x1F Thermo sensor # 0x1F Thermo sensor
sub getSensorType{ sub getSensorType{
return $_[0] & 0x1F; return ($_[0] & 0x1F);
} }
# decrypt bytes of hex string
# in: hex string
# out: decrypted hex string
sub decryptBytes{
my $Hidekihex=shift;
#create array of hex string
my @Hidekibytes = map { Hideki_decryptByte(hex($_)) } ($Hidekihex =~ /(..)/g);
my $result="75"; # Byte 0 is not encrypted
for (my $i=1; $i<scalar (@Hidekibytes); $i++){
$result.=sprintf("%02x",$Hidekibytes[$i]);
}
return $result;
}
sub Hideki_decryptByte{
my $byte = shift;
#printf("\ndecryptByte 0x%02x >>",$byte);
my $ret2 = ($byte ^ ($byte << 1) & 0xFF); #gives possible overflow to left so c3->145 instead of 45
#printf(" %02x\n",$ret2);
return $ret2;
}
# decode byte array and return channel, temperature # decode byte array and return channel, temperature
# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); # input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array);
@ -534,7 +541,7 @@ Hideki_Attr(@)
&lt;code&gt; besteht aus dem Sensortyp und der Kanalnummer (1..5) oder wenn das Attribut longid im IO Device gesetzt ist aus einer Zufallsadresse, die durch den Sensor beim einlegen der &lt;code&gt; besteht aus dem Sensortyp und der Kanalnummer (1..5) oder wenn das Attribut longid im IO Device gesetzt ist aus einer Zufallsadresse, die durch den Sensor beim einlegen der
Batterie generiert wird (Die Adresse aendert sich bei jedem Batteriewechsel).<br> Batterie generiert wird (Die Adresse aendert sich bei jedem Batteriewechsel).<br>
</li> </li>
<li>Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzuf&uumlgen.</li> <li>Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzuf&uuml;gen.</li>
</ul> </ul>
<br> <br>
@ -548,7 +555,7 @@ Hideki_Attr(@)
<li>battery (ok oder low)</li> <li>battery (ok oder low)</li>
<li>channel (Der Sensor Kanal)</li> <li>channel (Der Sensor Kanal)</li>
<br><i>- Hideki spezifisch -</i> <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>comfort_level (Status: Humidity OK... , Wet gr&ouml;&szlig;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> <li>package_number (Paketnummer in der letzten Signalfolge, startet bei 1)</li><br>
</ul> </ul>
<a name="Hideki_unset"></a> <a name="Hideki_unset"></a>