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:
parent
a09bf2c735
commit
d99cfd2626
@ -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
|
||||
|
@ -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,28 +80,28 @@ 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;
|
||||
@ -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(@)
|
||||
<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).<br>
|
||||
</li>
|
||||
<li>Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzufügen.</li>
|
||||
<li>Wenn autocreate aktiv ist, dann wird der Sensor automatisch in FHEM angelegt. Das ist der empfohlene Weg, neue Sensoren hinzuzufü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öß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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user