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.
# 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

View File

@ -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
#convert dectypted hex str back to array of bytes:
my @decodedBytes = map { hex($_) } ($decodedString =~ /(..)/g);
if ($crc1crc2OK == 0) {
return ''; #crc1 or crc2 failed
}
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;
# 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
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);
#}
#/* 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);
}
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
if($data[0] != 0x75) {
Log3 $iohash, 4, "$name Hideki_Parse: rawData=$rawData is no Hideki";
return (0,@data);
}
my $count=($Hidekibytes[2]>>1) & 0x1f;
my $b;
#iterate over data only, first byte is 0x75 always
for (my $i=1; $i<$count+2 && $i<scalar @Hidekibytes; $i++) {
$b = $Hidekibytes[$i];
$cs1 = $cs1 ^ $b; # calc first chksum
# read bytes 1 to n-2 , just before checksum
for ($i=1; $i<($count+2); $i++) {
$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;
}
else{
return 0;
$count += 2;
if ($cs1 != 0 || $cs2 != $data[$count]) {
Log3 $iohash, 4, "$name Hideki crcCheck FAILED: cs1 / cs2/checksum2 $cs1 / $cs2/$data[$count], rawData=$rawData, count+2=$count, length=$L";
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
# 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<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
# 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
Batterie generiert wird (Die Adresse aendert sich bei jedem Batteriewechsel).<br>
</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>
<br>
@ -548,7 +555,7 @@ Hideki_Attr(@)
<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>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>
</ul>
<a name="Hideki_unset"></a>