From fdeccf4d8b1af9a487c1b8c145135cb92f40e9a0 Mon Sep 17 00:00:00 2001 From: mrsidey <> Date: Thu, 22 Oct 2015 20:55:57 +0000 Subject: [PATCH] Hideki: Added module for integration weather sensors using hideki protocol into fhem git-svn-id: https://svn.fhem.de/fhem/trunk@9602 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/fhem/14_Hideki.pm | 425 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+) create mode 100644 fhem/fhem/14_Hideki.pm diff --git a/fhem/fhem/14_Hideki.pm b/fhem/fhem/14_Hideki.pm new file mode 100644 index 000000000..3f7cf2f0d --- /dev/null +++ b/fhem/fhem/14_Hideki.pm @@ -0,0 +1,425 @@ +############################################## +# $Id: 14_Hideki.pm 12128 2015-10-12 $ +# The file is taken from the SIGNALduino project +# see http://www.fhemwiki.de/wiki/SIGNALduino +# and was modified by a few additions +# to support Hideki Sensors +# S. Butzek & HJGode &Ralf9 2015 +# + +package main; + +use strict; +use warnings; +use POSIX; + +#use Data::Dumper; + +##################################### +sub +Hideki_Initialize($) +{ + my ($hash) = @_; + + + $hash->{Match} = "^P12#75[A-F0-9]{17,30}"; # Länge (Anhahl nibbles nach 0x75 )noch genauer zpezifiieren + $hash->{DefFn} = "Hideki_Define"; + $hash->{UndefFn} = "Hideki_Undef"; + $hash->{AttrFn} = "Hideki_Attr"; + $hash->{ParseFn} = "Hideki_Parse"; + $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 " + ."ignore:0,1 " + ." $readingFnAttributes"; + + $hash->{AutoCreate}= + { "Hideki.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} }; + +} + + +##################################### +sub +Hideki_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define Hideki ".int(@a) + if(int(@a) < 3); + + $hash->{CODE} = $a[2]; + $hash->{lastMSG} = ""; + + my $name= $hash->{NAME}; + + $modules{Hideki}{defptr}{$a[2]} = $hash; + $hash->{STATE} = "Defined"; + + #AssignIoPort($hash); + return undef; +} + +##################################### +sub +Hideki_Undef($$) +{ + my ($hash, $name) = @_; + delete($modules{Hideki}{defptr}{$hash->{CODE}}) if($hash && $hash->{CODE}); + return undef; +} + + +##################################### +sub +Hideki_Parse($$) +{ + my ($iohash,$msg) = @_; + my (undef ,$rawData) = split("#",$msg); + + my $name = $iohash->{NAME}; + my @a = split("", $msg); + Log3 $iohash, 4, "Hideki_Parse $name incomming $msg"; + + # decrypt bytes + my $decodedString = decryptBytes($rawData); # decrpyt hex string to hex string + + #convert dectypted hex str back to array of bytes: + my @decodedBytes = map { hex($_) } ($decodedString =~ /(..)/g); + + if (!@decodedBytes) + { + Log3 $iohash, 4, "$name decrypt failed"; + return undef; + } + if (!Hideki_crc(\@decodedBytes)) + { + Log3 $iohash, 4, "$name crc failed"; + return undef; + } + my $sensorTyp=getSensorType($decodedBytes[3]); + Log3 $iohash, 4, "Hideki_Parse SensorTyp = $sensorTyp decodedString = $decodedString"; + my $id=substr($decodedString,2,2); # get the random id from the data + my $channel=0; + my $temp=0; + my $hum=0; + my $rc; + my $val; + my $bat; + my $deviceCode; + my $model= "Hideki_$sensorTyp"; + + ## 1. Detect what type of sensor we have, then calll specific function to decode + if ($sensorTyp==0x1E){ + ($channel, $temp, $hum) = decodeThermoHygro(\@decodedBytes); # decodeThermoHygro($decodedString); + $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery + $val = "T: $temp H: $hum Bat: $bat"; + }else{ + Log3 $iohash, 4, "$name Sensor Typ $sensorTyp not supported, please report sensor information!"; + return "$name Sensor Typ $sensorTyp not supported, please report sensor information!"; + } + my $longids = AttrVal($iohash->{NAME},'longids',0); + if ( ($longids != 0) && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) + { + $deviceCode = $model . "_" . $id; + Log3 $iohash,4, "$name using longid: $longids model: $model"; + } else { + $deviceCode = $model . "_" . $channel; + } + + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, temp=$temp, humidity=$hum, bat=$bat"; + Log3 $iohash, 5, "deviceCode: $deviceCode"; + + my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode}; + $def = $modules{Hideki}{defptr}{$deviceCode} if(!$def); + + if(!$def) { + Log3 $iohash, 1, "Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode"; + return "UNDEFINED $deviceCode Hideki $deviceCode"; + } + + my $hash = $def; + $name = $hash->{NAME}; + return "" if(IsIgnored($name)); + + #Log3 $name, 4, "Hideki: $name ($msg)"; + + + if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef))) + { + 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"; + return ""; + } + } + $hash->{lastReceive} = time(); + + $def->{lastMSG} = $decodedString; + + #Log3 $name, 4, "Hideki update $name:". $name; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "state", $val); + readingsBulkUpdate($hash, "battery", $bat) if ($bat ne ""); + readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne ""); + readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne ""); + readingsEndUpdate($hash, 1); # Notify is done by Dispatch + + 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; + #iterate over data only, first byte is 0x75 always + for (my $i=1; $i<$count+2 && $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 and hunidity +# input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); +# output , , , +# was unable to get this working with an array ref as input, so switched to hex string input +sub decodeThermoHygro { + 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 $temp=0; + my $humi=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? + if (!($Hidekibytes[5] & 0x80)) { + $temp = -$temp; + } + + $humi = 10 * ($Hidekibytes[6] >> 4) + ($Hidekibytes[6] & 0x0f); + + $temp = $temp / 10; + return ($channel, $temp, $humi); +} + +sub +Hideki_Attr(@) +{ + my @a = @_; + + # Make possible to use the same code for different logical devices when they + # are received through different physical devices. + return if($a[0] ne "set" || $a[2] ne "IODev"); + my $hash = $defs{$a[1]}; + my $iohash = $defs{$a[3]}; + my $cde = $hash->{CODE}; + delete($modules{Hideki}{defptr}{$cde}); + $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $cde} = $hash; + return undef; +} + + +1; + +=pod +=begin html + + +

Hideki

+
    + The Hideki module is a module for decoding weather sensors, which use the hideki protocol. Known brands are Bresser, Cresta, TFA and Hama. +

    + + + Supported Brands +
      +
    • Hama
    • +
    • Bresser
    • +
    • TFA Dostman
    • +
    • Arduinos with remote Sensor lib from Randy Simons
    • +
    • Cresta
    • +
    • Hideki
    • +
    • all other devices, which use the Hideki protocol
    • +
    + Please note, currently temp/hum devices are implemented. Please report data for other sensortypes. + + + Define +
      + define <name> Hideki <code>
      + +
      + <code> is the address of the sensor device and + is build by the sensor type and the channelnumber (1 to 5) or if the attribute longid is specfied an autogenerated address build when inserting + the battery (this adress will change every time changing the battery).
      + + If autocreate is enabled, the device will be defined via autocreate. This is also the preferred mode of defining such a device. + +
    + + Generated readings +
      +
    • state (T:x H:y B:z)
    • +
    • temperature (°C)
    • +
    • humidity (0-100)
    • +
    • battery (ok or low)
    • +
    + + + + Set
      N/A

    + + + Get
      N/A

    + + + Attributes + +
    +
+ +=end html + +=begin html_DE + + +

Hideki

+
    + Das Hideki module dekodiert empfangene Nachrichten von Wettersensoren, welche das Hideki Protokoll verwenden. +

    + + + Unterstützte Hersteller +
      +
    • Hama
    • +
    • Bresser
    • +
    • TFA Dostman
    • +
    • Arduinos with remote Sensor lib from Randy Simons
    • +
    • Cresta
    • +
    • Hideki
    • +
    • Alle anderen, welche das Hideki Protokoll verwenden
    • +
    + Hinweis, Aktuell sind nur temp/feuchte Sensoren implementiert. Bitte sendet uns Daten zu anderen Sensoren. + + + Define +
      +
    • define <name> Hideki <code>
    • +
    • +
      + <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.
    • + +
    +
    + + + Erzeugte Readings +
      +
    • state (T:x H:y B:z)
    • +
    • temperature (°C)
    • +
    • humidity (0-100)
    • +
    • battery (ok or low)
    • +
    + + Set
      N/A

    + + + Get
      N/A

    + + + Attribute + +
    +
+ +=end html_DE +=cut