diff --git a/fhem/contrib/98_WKRCD4.pm b/fhem/contrib/98_WKRCD4.pm index 5434bca41..7ee70efc5 100644 --- a/fhem/contrib/98_WKRCD4.pm +++ b/fhem/contrib/98_WKRCD4.pm @@ -3,33 +3,58 @@ # Vorlage: Modul WHR962, diverse Foreneinträge sowie Artikel über Auswertung der # Wärmepumpe mit Linux / Perl im Linux Magazin aus 2010 # insbesondere: -# http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=2 (Speicheradressen-Liste) -# http://www.ip-symcon.de/forum/threads/2092-ComPort-und-Waterkotte-abfragen (Protokollbeschreibung) -# http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=4 (Beispiel Befehls-Strings) -# - +# http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=2 (Speicheradressen-Liste) +# http://www.ip-symcon.de/forum/threads/2092-ComPort-und-Waterkotte-abfragen (Protokollbeschreibung) +# http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=4 (Beispiel Befehls-Strings) +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################################################## +# Changelog: +# +# 2013-11-1 initial version +# 2013-12-25 bug fixes, GPL comment added, loglevel reading removed +# 2014-4-7 performance tuning, read handling with binary string instead of hex string +# 2014-5-22 decode mode - heating, cooling, warm water on / off +# 2015-7-23 added utf8 encoding of state string, Added name to Log3 calls +# + package main; use strict; use warnings; -use Time::HiRes qw(gettimeofday); +use Time::HiRes qw(gettimeofday); +use Encode qw(decode encode); # # list of Readings / values that can explicitely be requested # from the WP with the GET command -my %WKRCD4_gets = ( - "Hzg-TempBasisSoll" => "Hzg-TempBasisSoll", - "WW-Temp-Soll" => "Temp-WW-Soll" +my %WKRCD4_gets = ( + "Hzg-TempBasisSoll" => "Hzg-TempBasisSoll", + "WW-Temp-Soll" => "Temp-WW-Soll" ); # list of Readings / values that can be written to the WP -my %WKRCD4_sets = ( - "Hzg-TempBasisSoll" => "Hzg-TempBasisSoll" +my %WKRCD4_sets = ( + "Hzg-TempBasisSoll" => "Hzg-TempBasisSoll" ); # Definition of the values that can be read / written # with the relative address, number of bytes and -# fmat to be used in sprintfd when formatting the value +# fmat to be used in sprintf when formatting the value # unp to be used in pack / unpack commands # min / max for setting values # @@ -85,15 +110,18 @@ my %frameReadings = ( 'BetrStundenWwPu' => { addr => 0x0072, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' }, 'BetrStundenSt2' => { addr => 0x0076, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' }, 'Zeit' => { addr => 0x0064, bytes => 0x0006, fmat=> '%4$02d.%5$02d.%6$02d %3$02d:%2$02d:%1$02d', unp => 'CCCCCC'}, - 'SetBetriebsMode' => { addr => 0x014E, bytes => 0x0003, unp => 'N', pack => 'xC*'}, - 'Display-Zeile-1' => { addr => 0x008E, bytes => 0x0002, unp => 'n' }, - 'Display-Zeile-2' => { addr => 0x0090, bytes => 0x0001, unp => 'C' }, - 'Status-Gesamt' => { addr => 0x00D2, bytes => 0x0001, unp => 'C' }, + 'SetBetriebsMode' => { addr => 0x014E, bytes => 0x0003, unp => 'N', pack => 'xC*'}, + 'Display-Zeile-1' => { addr => 0x008E, bytes => 0x0002, unp => 'n' }, + 'Display-Zeile-2' => { addr => 0x0090, bytes => 0x0001, unp => 'C' }, + 'Status-Gesamt' => { addr => 0x00D2, bytes => 0x0001, unp => 'C' }, 'Status-Heizung' => { addr => 0x00D4, bytes => 0x0003, unp => 'B24' }, 'Status-Kuehlung' => { addr => 0x00DA, bytes => 0x0003, unp => 'B24' }, 'Mode-Heizung' => { addr => 0x00DF, bytes => 0x0001, unp => 'B8' }, 'Mode-Kuehlung' => { addr => 0x00E0, bytes => 0x0001, unp => 'B8' }, - 'Mode-Warmwasser' => { addr => 0x00E1, bytes => 0x0001, unp => 'B8' } + 'Mode-Warmwasser' => { addr => 0x00E1, bytes => 0x0001, unp => 'B8' }, + 'Heizung' => { addr => 0x00DF, bytes => 0x0001, unp => 'b' }, + 'Kuehlung' => { addr => 0x00E0, bytes => 0x0001, unp => 'b' }, + 'Warmwasser' => { addr => 0x00E1, bytes => 0x0001, unp => 'b' } ); # @@ -102,69 +130,69 @@ my %frameReadings = ( ######################################################################### sub WKRCD4_Initialize($) { - my ($hash) = @_; + my ($hash) = @_; - require "$attr{global}{modpath}/FHEM/DevIo.pm"; + require "$attr{global}{modpath}/FHEM/DevIo.pm"; - $hash->{ReadFn} = "WKRCD4_Read"; - $hash->{ReadyFn} = "WKRCD4_Ready"; - $hash->{DefFn} = "WKRCD4_Define"; - $hash->{UndefFn} = "WKRCD4_Undef"; - $hash->{SetFn} = "WKRCD4_Set"; - $hash->{GetFn} = "WKRCD4_Get"; - $hash->{AttrList} = - "do_not_notify:1,0 loglevel:0,1,2,3,4,5,6 " . $readingFnAttributes; + $hash->{ReadFn} = "WKRCD4_Read"; + $hash->{ReadyFn} = "WKRCD4_Ready"; + $hash->{DefFn} = "WKRCD4_Define"; + $hash->{UndefFn} = "WKRCD4_Undef"; + $hash->{SetFn} = "WKRCD4_Set"; + $hash->{GetFn} = "WKRCD4_Get"; + $hash->{AttrList} = + "do_not_notify:1,0 " . $readingFnAttributes; } # # Define command # init internal values, open device, # set internal timer to send read command / wakeup -######################################################################### # +######################################################################### # sub WKRCD4_Define($$) { - my ( $hash, $def ) = @_; - my @a = split( "[ \t][ \t]*", $def ); + my ( $hash, $def ) = @_; + my @a = split( "[ \t][ \t]*", $def ); - return "wrong syntax: define WKRCD4 [devicename|none] interval" - if ( @a < 3 ); + return "wrong syntax: define WKRCD4 [devicename\@speed|none] [interval]" + if ( @a < 3 ); - DevIo_CloseDev($hash); - my $name = $a[0]; - my $dev = $a[2]; - my $interval = 60; - - if ( $dev eq "none" ) { - Log3 undef, 1, "WKRCD4 device is none, commands will be echoed only"; - return undef; - } - - if(int(@a) == 4) { - $interval= $a[3]; - if ($interval < 20) { - return "interval too small, please use something > 20, default is 60"; - } - } + DevIo_CloseDev($hash); + my $name = $a[0]; + my $dev = $a[2]; + my $interval = 60; + + if ( $dev eq "none" ) { + Log3 undef, 1, "$name: device is none, commands will be echoed only"; + return undef; + } + + if(int(@a) == 4) { + $interval= $a[3]; + if ($interval < 20) { + return "interval too small, please use something > 20, default is 60"; + } + } - $hash->{buffer} = ""; - - $hash->{DeviceName} = $dev; - $hash->{INTERVAL} = $interval; + $hash->{buffer} = ""; + + $hash->{DeviceName} = $dev; + $hash->{INTERVAL} = $interval; - $hash->{SerialRequests} = 0; - $hash->{SerialGoodReads} = 0; - $hash->{SerialBadReads} = 0; + $hash->{SerialRequests} = 0; + $hash->{SerialGoodReads} = 0; + $hash->{SerialBadReads} = 0; - # send wakeup string (read 2 values preceeded with AT) - $hash->{LastRequestAdr} = 8; - $hash->{LastRequestLen} = 4; - $hash->{LastRequest} = gettimeofday(); - my $ret = DevIo_OpenDev( $hash, 0, "WKRCD4_Wakeup" ); - - # initial read after 3 secs, there timer is set to interval for update and wakeup - InternalTimer(gettimeofday()+3, "WKRCD4_GetUpdate", $hash, 0); + # send wakeup string (read 2 values preceeded with AT) + $hash->{LastRequestAdr} = 8; + $hash->{LastRequestLen} = 4; + $hash->{LastRequest} = gettimeofday(); + my $ret = DevIo_OpenDev( $hash, 0, "WKRCD4_Wakeup" ); + + # initial read after 3 secs, there timer is set to interval for update and wakeup + InternalTimer(gettimeofday()+3, "WKRCD4_GetUpdate", $hash, 0); - return $ret; + return $ret; } # @@ -172,10 +200,10 @@ sub WKRCD4_Define($$) ######################################################################### sub WKRCD4_Undef($$) { - my ( $hash, $arg ) = @_; - DevIo_CloseDev($hash); - RemoveInternalTimer($hash); - return undef; + my ( $hash, $arg ) = @_; + DevIo_CloseDev($hash); + RemoveInternalTimer($hash); + return undef; } @@ -183,12 +211,12 @@ sub WKRCD4_Undef($$) # Encode the data to be sent to the device (0x10 gets doubled) ######################################################################### sub Encode10 (@) { - my @a = (); - for my $byte (@_) { - push @a, $byte; - push @a, $byte if $byte == 0x10; - } - return @a; + my @a = (); + for my $byte (@_) { + push @a, $byte; + push @a, $byte if $byte == 0x10; + } + return @a; } # @@ -196,20 +224,20 @@ sub Encode10 (@) { ######################################################################### sub WPCMD($$$$;@) { - my ($hash, $cmd, $addr, $len, @value ) = @_; - my $name = $hash->{NAME}; - my @frame = (); - - if ($cmd eq "read") { - @frame = (0x01, 0x15, Encode10($addr>>8, $addr%256), Encode10($len>>8, $len%256)); - } elsif ($cmd eq "write") { - @frame = (0x01, 0x13, Encode10($addr>>8, $addr%256), Encode10(@value)); - } else { - Log3 $name, 3, "undefined cmd ($cmd) in WPCMD"; - return 0; - } - my $crc = CRC16(@frame); - return (0xff, 0x10, 0x02, @frame, 0x10, 0x03, $crc >> 8, $crc % 256, 0xff); + my ($hash, $cmd, $addr, $len, @value ) = @_; + my $name = $hash->{NAME}; + my @frame = (); + + if ($cmd eq "read") { + @frame = (0x01, 0x15, Encode10($addr>>8, $addr%256), Encode10($len>>8, $len%256)); + } elsif ($cmd eq "write") { + @frame = (0x01, 0x13, Encode10($addr>>8, $addr%256), Encode10(@value)); + } else { + Log3 $name, 3, "$name: undefined cmd ($cmd) in WPCMD"; + return 0; + } + my $crc = CRC16(@frame); + return (0xff, 0x10, 0x02, @frame, 0x10, 0x03, $crc >> 8, $crc % 256, 0xff); } # @@ -217,93 +245,93 @@ sub WPCMD($$$$;@) ######################################################################### sub WKRCD4_Get($@) { - my ( $hash, @a ) = @_; - return "\"get WKRCD4\" needs at least an argument" if ( @a < 2 ); + my ( $hash, @a ) = @_; + return "\"get WKRCD4\" needs at least an argument" if ( @a < 2 ); - my $name = shift @a; - my $attr = shift @a; - my $arg = join("", @a); - - if(!$WKRCD4_gets{$attr}) { - my @cList = keys %WKRCD4_gets; - return "Unknown argument $attr, choose one of " . join(" ", @cList); - } + my $name = shift @a; + my $attr = shift @a; + my $arg = join("", @a); + + if(!$WKRCD4_gets{$attr}) { + my @cList = keys %WKRCD4_gets; + return "Unknown argument $attr, choose one of " . join(" ", @cList); + } - # get Hash pointer for the attribute requested from the global hash - my $properties = $frameReadings{$WKRCD4_sets{$attr}}; - if(!$properties) { - return "No Entry in frameReadings found for $attr"; - } + # get Hash pointer for the attribute requested from the global hash + my $properties = $frameReadings{$WKRCD4_gets{$attr}}; + if(!$properties) { + return "No Entry in frameReadings found for $attr"; + } - # get details about the attribute requested from its hash - my $addr = $properties->{addr}; - my $bytes = $properties->{bytes}; - Log3 $name, 4, sprintf ("Read %02x bytes starting from %02x for $attr", $bytes, $addr); + # get details about the attribute requested from its hash + my $addr = $properties->{addr}; + my $bytes = $properties->{bytes}; + Log3 $name, 4, sprintf ("$name: Get will read %02x bytes starting from %02x for $attr", $bytes, $addr); - # create command for WP - my $cmd = pack('C*', WPCMD($hash, 'read', $addr, $bytes)); + # create command for WP + my $cmd = pack('C*', WPCMD($hash, 'read', $addr, $bytes)); - # set internal variables to track what is happending - $hash->{LastRequestAdr} = $addr; - $hash->{LastRequestLen} = $bytes; - $hash->{LastRequest} = gettimeofday(); - $hash->{SerialRequests}++; + # set internal variables to track what is happending + $hash->{LastRequestAdr} = $addr; + $hash->{LastRequestLen} = $bytes; + $hash->{LastRequest} = gettimeofday(); + $hash->{SerialRequests}++; - Log3 $name, 4, "Get -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); - DevIo_SimpleWrite( $hash, $cmd , 0 ); - - return sprintf ("Read %02x bytes starting from %02x", $bytes, $addr); + Log3 $name, 4, "$name: Get -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); + DevIo_SimpleWrite( $hash, $cmd , 0 ); + + return sprintf ("Read %02x bytes starting from %02x", $bytes, $addr); } - + # # SET command ######################################################################### sub WKRCD4_Set($@) { - my ( $hash, @a ) = @_; - return "\"set WKRCD4\" needs at least an argument" if ( @a < 2 ); + my ( $hash, @a ) = @_; + return "\"set WKRCD4\" needs at least an argument" if ( @a < 2 ); - my $name = shift @a; - my $attr = shift @a; - my $arg = join("", @a); - - if(!defined($WKRCD4_sets{$attr})) { - my @cList = keys %WKRCD4_sets; - return "Unknown argument $attr, choose one of " . join(" ", @cList); - } + my $name = shift @a; + my $attr = shift @a; + my $arg = join("", @a); + + if(!defined($WKRCD4_sets{$attr})) { + my @cList = keys %WKRCD4_sets; + return "Unknown argument $attr, choose one of " . join(" ", @cList); + } - # get Hash pointer for the attribute requested from the global hash - my $properties = $frameReadings{$WKRCD4_sets{$attr}}; - if(!$properties) { - return "No Entry in frameReadings found for $attr"; - } + # get Hash pointer for the attribute requested from the global hash + my $properties = $frameReadings{$WKRCD4_sets{$attr}}; + if(!$properties) { + return "No Entry in frameReadings found for $attr"; + } - # get details about the attribute requested from its hash - my $addr = $properties->{addr}; - my $bytes = $properties->{bytes}; - my $min = $properties->{min}; - my $max = $properties->{max}; - my $unp = $properties->{unp}; - + # get details about the attribute requested from its hash + my $addr = $properties->{addr}; + my $bytes = $properties->{bytes}; + my $min = $properties->{min}; + my $max = $properties->{max}; + my $unp = $properties->{unp}; + return "a numerical value between $min and $max is expected, got $arg instead" if($arg !~ m/^[\d.]+$/ || $arg < $min || $arg > $max); - - # convert string to value needed for command - my $vp = pack($unp, $arg); - my @value = unpack ('C*', $vp); - - Log3 $name, 4, sprintf ("Write $attr: %02x bytes starting from %02x with %s (%s) packed with $unp", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp)); - my $cmd = pack('C*', WPCMD($hash, 'write', $addr, $bytes, @value)); - - # set internal variables to track what is happending - $hash->{LastRequestAdr} = $addr; - $hash->{LastRequestLen} = $bytes; - $hash->{LastRequest} = gettimeofday(); - $hash->{SerialRequests}++; - Log3 $name, 4, "Set -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); - DevIo_SimpleWrite( $hash, $cmd , 0 ); - - return sprintf ("Wrote %02x bytes starting from %02x with %s (%s)", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp)); + + # convert string to value needed for command + my $vp = pack($unp, $arg); + my @value = unpack ('C*', $vp); + + Log3 $name, 4, sprintf ("$name: Set will write $attr: %02x bytes starting from %02x with %s (%s) packed with $unp", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp)); + my $cmd = pack('C*', WPCMD($hash, 'write', $addr, $bytes, @value)); + + # set internal variables to track what is happending + $hash->{LastRequestAdr} = $addr; + $hash->{LastRequestLen} = $bytes; + $hash->{LastRequest} = gettimeofday(); + $hash->{SerialRequests}++; + Log3 $name, 4, "Set -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); + DevIo_SimpleWrite( $hash, $cmd , 0 ); + + return sprintf ("Wrote %02x bytes starting from %02x with %s (%s)", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp)); } @@ -312,64 +340,62 @@ sub WKRCD4_Set($@) # called from the global loop, when the select for hash->{FD} reports data sub WKRCD4_Read($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - # read from serial device - my $buf = DevIo_SimpleRead($hash); - return "" if ( !defined($buf) ); + my ($hash) = @_; + my $name = $hash->{NAME}; + + # read from serial device + my $buf = DevIo_SimpleRead($hash); + return "" if ( !defined($buf) ); - # convert to hex string to make parsing with regex easier - $hash->{buffer} .= unpack ('H*', $buf); - Log3 $name, 5, "Current buffer content: " . $hash->{buffer}; + $hash->{buffer} .= $buf; + Log3 $name, 5, "$name: read buffer content: " . unpack ('H*', $hash->{buffer}); - # did we already get a full frame? - if ($hash->{buffer} =~ "ff1002(.{4})(.*)1003(.{4})ff(.*)") - { - my $msg = $1; - my $frame = $msg . $2; - my $crc = $3; - - Log3 $name, 4, "Match msg: " .$msg . " " . $frame . " CRC " . $crc . " Rest " . $4; - $hash->{buffer} = $4; + # did we already get a full frame? + if ( $hash->{buffer} !~ /\xff\x10\x02(.{2})(.*)\x10\x03(.{2})\xff(.*)/s ) + { + Log3 $name, 5, "$name: read NoMatch: " . unpack ('H*', $hash->{buffer}); + return ""; + } + my $msg = unpack ('H*', $1); + my @aframe = unpack ('C*', $1 . $2); + my $crc = unpack ('S>', $3); + my $rest = $4; + + $hash->{buffer} = $rest; + Log3 $name, 4, "$name: read match msg: $msg CRC $crc"; + Log3 $name, 5, "$name: read frame is " . unpack ('H*', pack ('C*', @aframe)) . ", Rest " . unpack ('H*', $rest); - # convert frame contents to byte array - my @aframe = unpack ('C*', pack ('H*', $frame)); - - # calculate CRC and compare with CRC from read - my $crc2 = sprintf("%04x",CRC16(@aframe)); - if ($crc eq $crc2) - { - Log3 $name, 4, "CRC Ok."; - $hash->{SerialGoodReads}++; - - # reply to read request ? - if ($msg eq "0017") { - my @data; - for(my $i=0,my $offset=2;$offset<=$#aframe;$offset++,$i++) - { - # remove duplicate 0x10 (frames are encoded like this) - if (($aframe[$offset]==16)&&($aframe[$offset+1]==16)) { $offset++; } - $data[$i] = $aframe[$offset]; - } - Log3 $name, 4, "Parse with relative request start " . $hash->{LastRequestAdr} . " Len " . $hash->{LastRequestLen}; - # extract values from data - parseReadings($hash, @data); - } elsif ($msg eq "0011") { - # reply to write - } else { - Log3 $name, 3, "Unknown Msg type " . $msg . " in " . $hash->{buffer}; - } - } else - { - Log3 $name, 3, "Bad CRC from WP: " . $crc . " berechnet: " . $crc2 . " Frame ". $frame; - $hash->{SerialBadReads} ++; - }; - @aframe = (); - } else { - Log3 $name, 5, "NoMatch: " . $hash->{buffer}; - }; - return ""; + # calculate CRC and compare with CRC from read + my $crc2 = CRC16(@aframe); + if ($crc != $crc2) { + Log3 $name, 3, "$name: read Bad CRC from WP: $crc, berechnet: $crc2"; + Log3 $name, 4, "$name: read Frame was " . unpack ('H*', pack ('C*', @aframe)); + $hash->{SerialBadReads} ++; + @aframe = (); + return ""; + }; + Log3 $name, 4, "$name: read CRC Ok."; + $hash->{SerialGoodReads}++; + + # reply to read request ? + if ($msg eq "0017") { + my @data; + for(my $i=0,my $offset=2;$offset<=$#aframe;$offset++,$i++) + { + # remove duplicate 0x10 (frames are encoded like this) + if (($aframe[$offset]==16)&&($aframe[$offset+1]==16)) { $offset++; } + $data[$i] = $aframe[$offset]; + } + Log3 $name, 4, "$name: read -> Parse with relative request start " . $hash->{LastRequestAdr} . " Len " . $hash->{LastRequestLen}; + # extract values from data + parseReadings($hash, @data); + } elsif ($msg eq "0011") { + # reply to write + } else { + Log3 $name, 3, "$name: read got unknown Msg type " . $msg . " in " . $hash->{buffer}; + } + @aframe = (); + return ""; } # @@ -377,15 +403,15 @@ sub WKRCD4_Read($) ######################################################################### sub WKRCD4_Ready($) { - my ($hash) = @_; + my ($hash) = @_; - return DevIo_OpenDev( $hash, 1, undef ) - if ( $hash->{STATE} eq "disconnected" ); + return DevIo_OpenDev( $hash, 1, undef ) + if ( $hash->{STATE} eq "disconnected" ); - # This is relevant for windows/USB only - my $po = $hash->{USBDev}; - my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status; - return ( $InBytes > 0 ); + # This is relevant for windows/USB only + my $po = $hash->{USBDev}; + my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status; + return ( $InBytes > 0 ); } # @@ -393,20 +419,20 @@ sub WKRCD4_Ready($) ######################################################################### sub WKRCD4_Wakeup($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - $hash->{SerialRequests}++; - - $hash->{LastRequestAdr} = 8; - $hash->{LastRequestLen} = 4; - $hash->{LastRequest} = gettimeofday(); - - my $cmd = "41540D100201150008000410037EA010020115003000041003FDC3100201150034000410037D90"; - DevIo_SimpleWrite( $hash, $cmd , 1 ); - - Log3 $name, 5, "sent wakeup string: " . $cmd . " done."; - return undef; + my ($hash) = @_; + my $name = $hash->{NAME}; + + $hash->{SerialRequests}++; + + $hash->{LastRequestAdr} = 8; + $hash->{LastRequestLen} = 4; + $hash->{LastRequest} = gettimeofday(); + + my $cmd = "41540D100201150008000410037EA010020115003000041003FDC3100201150034000410037D90"; + DevIo_SimpleWrite( $hash, $cmd , 1 ); + + Log3 $name, 5, "$name: sent wakeup string: " . $cmd . " done."; + return undef; } # @@ -414,23 +440,23 @@ sub WKRCD4_Wakeup($) ################################### sub WKRCD4_GetUpdate($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WKRCD4_GetUpdate", $hash, 1); - InternalTimer(gettimeofday()+$hash->{INTERVAL}/2, "WKRCD4_Wakeup", $hash, 1); + my ($hash) = @_; + my $name = $hash->{NAME}; + + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WKRCD4_GetUpdate", $hash, 1); + InternalTimer(gettimeofday()+$hash->{INTERVAL}/2, "WKRCD4_Wakeup", $hash, 1); - $hash->{SerialRequests}++; - - my $cmd = pack('C*', WPCMD($hash, 'read', 0, 0x170)); - $hash->{LastRequestAdr} = 0; - $hash->{LastRequestLen} = 0x170; - $hash->{LastRequest} = gettimeofday(); - DevIo_SimpleWrite( $hash, $cmd , 0 ); - - Log3 $name, 5, "GetUpdate -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); - - return 1; + $hash->{SerialRequests}++; + + my $cmd = pack('C*', WPCMD($hash, 'read', 0, 0x170)); + $hash->{LastRequestAdr} = 0; + $hash->{LastRequestLen} = 0x170; + $hash->{LastRequest} = gettimeofday(); + DevIo_SimpleWrite( $hash, $cmd , 0 ); + + Log3 $name, 5, "$name: GetUpdate -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd); + + return 1; } # @@ -459,45 +485,55 @@ sub CRC16 sub parseReadings { my ($hash, @data) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; - my $reqStart = $hash->{LastRequestAdr}; - my $reqLen = $hash->{LastRequestLen}; + my $reqStart = $hash->{LastRequestAdr}; + my $reqLen = $hash->{LastRequestLen}; - # get enough bytes? + # get enough bytes? if (@data >= $reqLen) { - readingsBeginUpdate($hash); - # go through all possible readings from global hash + readingsBeginUpdate($hash); + # go through all possible readings from global hash while (my ($reading, $property) = each(%frameReadings)) { - my $addr = $property->{addr}; - my $bytes = $property->{bytes}; - - # is reading inside data we got? + my $addr = $property->{addr}; + my $bytes = $property->{bytes}; + + # is reading inside data we got? if (($addr >= $reqStart) && - ($addr + $bytes <= $reqStart + $reqLen)) + ($addr + $bytes <= $reqStart + $reqLen)) { - my $Idx = $addr - $reqStart; - # get relevant slice from data array - my @slice = @data[$Idx .. $Idx + $bytes - 1]; - - # convert according to rules in global hash or defaults - my $pack = ($property->{pack}) ? $property->{pack} : 'C*'; - my $unpack = ($property->{unp}) ? $property->{unp} : 'H*'; - my $fmat = ($property->{fmat}) ? $property->{fmat} : '%s'; - #my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice))) . " packed with $pack, unpacked with $unpack, (hex " . unpack ('H*', pack ('C*', @slice)) . ") format $fmat"; - my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice))); - - readingsBulkUpdate( $hash, $reading, $value ); - Log3 $name, 4, "parse set reading $reading to $value" if (@data <= 20); + my $Idx = $addr - $reqStart; + # get relevant slice from data array + my @slice = @data[$Idx .. $Idx + $bytes - 1]; + + # convert according to rules in global hash or defaults + my $pack = ($property->{pack}) ? $property->{pack} : 'C*'; + my $unpack = ($property->{unp}) ? $property->{unp} : 'H*'; + my $fmat = ($property->{fmat}) ? $property->{fmat} : '%s'; + #my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice))) . " packed with $pack, unpacked with $unpack, (hex " . unpack ('H*', pack ('C*', @slice)) . ") format $fmat"; + my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice))); + + readingsBulkUpdate( $hash, $reading, $value ); + Log3 $name, 4, "$name: parse set reading $reading to $value" if (@data <= 20); } } - readingsEndUpdate( $hash, 1 ); - } + my $Status = "WP idle"; + if (ReadingsVal($name, "Heizung", 0)) { + $Status = sprintf ("Heizung %s", ReadingsVal ($name, "Temp-Vorlauf", 0)); + } elsif (ReadingsVal($name, "Kuehlung", 0)) { + $Status = sprintf ("Kühlung %s", ReadingsVal ($name, "Temp-Vorlauf", 0)); + } elsif (ReadingsVal($name, "Kuehlung", 0)) { + $Status = sprintf ("Warmwasser %s", ReadingsVal ($name, "Temp-WW", 0)); + } + $Status = encode ("utf8", $Status); + readingsBulkUpdate( $hash, "Status", $Status); + readingsEndUpdate( $hash, 1 ); + } else { - Log3 $name, 3, "Data len smaller than requested ($reqLen) : " . unpack ('H*', pack ('C*', @data)); + Log3 $name, 3, "$name: parse - data len smaller than requested ($reqLen) : " . unpack ('H*', pack ('C*', @data)); return 0; } }