From d99cfd2626703c69ffe9b6ec781be30fd6a8e289 Mon Sep 17 00:00:00 2001 From: sidey79 Date: Thu, 29 Mar 2018 19:41:57 +0000 Subject: [PATCH] 14_Hideki.pm: changed decrypting messages and crc check git-svn-id: https://svn.fhem.de/fhem/trunk@16509 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/14_Hideki.pm | 197 +++++++++++++++++++++-------------------- 2 files changed, 103 insertions(+), 95 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 3982bb1ce..b61ea6b7f 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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. + - feature: 14_Hideki: changed decrypting and improved crc check - change: 14_SD_WS_Maverick: reworked module, renamed readings added commandref - bugfix: 14_SD_WS07: Error retrieving rssi value fixed diff --git a/fhem/FHEM/14_Hideki.pm b/fhem/FHEM/14_Hideki.pm index 703c2fc08..80f192ed1 100644 --- a/fhem/FHEM/14_Hideki.pm +++ b/fhem/FHEM/14_Hideki.pm @@ -6,6 +6,7 @@ # to support Hideki Sensors # S. Butzek, HJGode, Ralf9 2015-2017 # +# changed the way crc and decrypt is used hjgode 20171129 package main; @@ -79,31 +80,31 @@ Hideki_Parse($$) my $name = $iohash->{NAME}; my @a = split("", $msg); - Log3 $iohash, 4, "Hideki_Parse $name incomming $msg"; + Log3 $iohash, 4, "$name Hideki_Parse: incomming $msg"; - # decrypt bytes - my $decodedString = decryptBytes($rawData); # decrpyt hex string to hex string + my @decodedData; + 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: - my @decodedBytes = map { hex($_) } ($decodedString =~ /(..)/g); - - if (!@decodedBytes) - { - Log3 $iohash, 4, "$name decrypt failed"; + # decrypt and decodedBytes are now done with decryptAndCheck + my $decodedString = join '', unpack('H*', pack('C*',@decodedData)); # get hex string + Log3 $iohash, 4, "$name Hideki_Parse: raw=$rawData, decoded=$decodedString"; + + if (!@decodedData) { + Log3 $iohash, 4, "$name Hideki_Parse: decrypt failed"; return ''; } - my $sensorTyp=getSensorType($decodedBytes[3]); - Log3 $iohash, 4, "Hideki_Parse SensorTyp = $sensorTyp decodedString = $decodedString"; - - if (!Hideki_crc(\@decodedBytes)) - { - Log3 $iohash, 4, "$name crc failed"; - return ''; - } + Log3 $iohash, 5, "$name Hideki_Parse: getSensorType for ".$decodedData[3]; + my $sensorTyp=($decodedData[3] & 0x1F); + Log3 $iohash, 4, "$name Hideki_Parse: SensorTyp = $sensorTyp decodedString = $decodedString"; my $id=substr($decodedString,2,2); # get the random id from the data - my $channel=0; + my $channel=0; my $temp=""; my $hum=0; my $rain=0; @@ -123,11 +124,11 @@ Hideki_Parse($$) my $comfort=0; ## 1. Detect what type of sensor we have, then call specific function to decode 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 - $count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter - $comfort = ($decodedBytes[7] >> 2 & 0x03); # comfort level + ($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)' } elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' } @@ -136,28 +137,28 @@ Hideki_Parse($$) $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 + ($channel, $temp) = decodeThermo(\@decodedData); + $bat = ($decodedData[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery + $count = $decodedData[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 - $count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter + ($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, "$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 + ($channel, $temp) = decodeThermo(\@decodedData); # decodeThermoHygro($decodedString); + ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedData); + $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"; 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 + ($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, "$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!"; @@ -170,18 +171,18 @@ Hideki_Parse($$) if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) { $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 { $deviceCode = $model . "_" . $channel; } - Log3 $iohash, 5, "deviceCode: $deviceCode"; + Log3 $iohash, 5, "$name Hideki_Parse deviceCode: $deviceCode"; my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode}; $def = $modules{Hideki}{defptr}{$deviceCode} 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"; } @@ -202,7 +203,7 @@ Hideki_Parse($$) { my $minsecs = AttrVal($iohash->{NAME},'minsecs',0); 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 ""; } } @@ -239,41 +240,69 @@ Hideki_Parse($$) return $name; } -# check crc for incoming message -# in: hex string with encrypted, raw data, starting with 75 -# out: 1 for OK, 0 for failed -# sample "75BDBA4AC2BEC855AC0A00" -sub Hideki_crc{ - #my $Hidekihex=shift; - #my @Hidekibytes=shift; - - my @Hidekibytes = @{$_[0]}; - #push @Hidekibytes,0x75; #first byte always 75 and will not be included in decrypt/encrypt! - #convert to array except for first hex - #for (my $i=1; $i<(length($Hidekihex))/2; $i++){ - # my $hex=Hideki_decryptByte(hex(substr($Hidekihex, $i*2, 2))); - # push (@Hidekibytes, $hex); - #} - - my $cs1=0; #will be zero for xor over all (bytes>>1)&0x1F except first byte (always 0x75) - #my $rawData=shift; - #todo add the crc check here - - my $count=($Hidekibytes[2]>>1) & 0x1f; - my $b; +# decryptAndCheck +# input is raw data (array of bytes) +# output is true if check1 and check2 OK +# data will then hold the decrypted data +sub decryptAndCheck { + my $iohash = shift; + my $rawData = shift; + my $name = $iohash->{NAME}; + my $cs1=0; #will be zero for xor over all (bytes[2]>>1)&0x1F except first byte (always 0x75) + my $cs2=0; + my $i; + my @data; + @data=map { hex($_) } ($rawData =~ /(..)/g); #byte array from raw hex data string + + #/* Decrypt raw received data byte */ BYTE DecryptByte(BYTE b) { return b ^ (b << 1); } + my $count=( ($data[2] ^ ($data[2]<<1)) >>1 ) & 0x1f; + my $L = scalar @data; + if ($L <= $count+2) { + Log3 $iohash, 4, "$name Hideki_crc: rawdata=$rawData to short, count=$count data length=$L"; + return (0,@data); + } + + 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 - for (my $i=1; $i<$count+2 && $i>1); + if (($b & 1) == 1){ + $c^=0x5f; + } + if (($c & 1) == 1){ + $b^=0x5f; + } + return ($b^($c>>1)); +} + + # return decoded sensor type # in: one byte # out: one byte @@ -285,31 +314,9 @@ sub Hideki_crc{ # 0x1E Thermo/hygro-sensor # 0x1F Thermo sensor 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>",$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 # input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); @@ -534,7 +541,7 @@ Hideki_Attr(@) <code> 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).
-
  • Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzufügen.
  • +
  • Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzufügen.

  • @@ -548,7 +555,7 @@ Hideki_Attr(@)
  • battery (ok oder low)
  • channel (Der Sensor Kanal)

  • - Hideki spezifisch - -
  • comfort_level (Status: Humidity OK... , Wet größer 69% RH, Dry weiniger als 40% RH, Temperature and humidity comfortable)
  • +
  • comfort_level (Status: Humidity OK... , Wet größer 69% RH, Dry weiniger als 40% RH, Temperature and humidity comfortable)
  • package_number (Paketnummer in der letzten Signalfolge, startet bei 1)