diff --git a/FHEM/23_LUXTRONIK2.pm b/FHEM/23_LUXTRONIK2.pm new file mode 100644 index 000000000..6c6b592fe --- /dev/null +++ b/FHEM/23_LUXTRONIK2.pm @@ -0,0 +1,429 @@ +################################################################ +# +# Copyright notice +# +# (c) 2012 Torsten Poitzsch (torsten.poitzsch@gmx.de) +# (c) 2012-2013 Jan-Hinrich Fessel (oskar@fessel.org) +# +# This script 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. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script 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. +# +# This copyright notice MUST APPEAR in all copies of the script! +# +################################################################ + +############################################## +package main; + +use strict; +use warnings; +use IO::Socket; + +my $cc; # The Itmes Changed Counter + +sub +LUXTRONIK2_Initialize($) +{ + my ($hash) = @_; + + $hash->{DefFn} = "LUXTRONIK2_Define"; + $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 firmware statusHTML"; +} + +sub +LUXTRONIK2_Define($$) +{ + my ($hash, $def) = @_; + my $name=$hash->{NAME}; + my @a = split("[ \t][ \t]*", $def); + + return "Wrong syntax: use define LUXTRONIK2 []" if(int(@a) <3 || int(@a) >4); + $hash->{Host} = $a[2]; + $hash->{INTERVAL}=$a[3] || 300; + + #Get first data after 5 seconds + InternalTimer(gettimeofday() + 5, "LUXTRONIK2_GetStatus", $hash, 0); + + return undef; +} + +sub +LUXTRONIK2_TempValueMerken($$$) +{ + my ($hash, $param, $paramName) = @_; + + $param /= 10; + if($hash->{READINGS}{$paramName}{VAL} != $param) { + $hash->{READINGS}{$paramName}{TIME} = TimeNow(); + $hash->{READINGS}{$paramName}{VAL} = $param; + $hash->{READINGS}{$paramName}{UNIT} = "Degree Celsius"; + $hash->{CHANGED}[$cc++] = $paramName .": ". $param; + } +} + +##################################### + +sub +LUXTRONIK2_GetStatus($) +{ + my ($hash) = @_; + my $err_log=''; + my @heatpump_values; + my @heatpump_parameters; + my $result=''; + my $switch=0; + my $value=''; + my $count=0; +# my $i=0; + my $name = $hash->{NAME}; + my $host = $hash->{Host}; + my $sensor = ''; + my $state = ''; + my $firmware; + my $serialno; + + $cc = 0; #initialize counter + + InternalTimer(gettimeofday() + $hash->{INTERVAL}, "LUXTRONIK2_GetStatus", $hash, 0); + + my $socket = new IO::Socket::INET ( PeerAddr => $host, + PeerPort => 8888, + # Type => SOCK_STREAM, # probably needed on some systems + Proto => 'tcp' + ); + if (!$socket) { + $hash->{STATE} = "error opening device"; + Log 1,"$name: Error opening Connection to $host"; + return "Can't Connect to $host -> $@ ( $!)\n"; + } + $socket->autoflush(1); + + #Read operational values + + $socket->send(pack("N", 3004)); + $socket->send(pack("N", 0)); + + # read response, should be 3004, status, number of parameters, and the parameters... + $socket->recv($result,4); + $count = unpack("N", $result); + if($count != 3004) { + Log 2, "LUXTRONIK2_GetStatus: $name $host 3004 Status problem 1: ".length($result)." -> ".$count; + return "3004 != 3004"; + } + + $socket->recv($result,4); + $count = unpack("N", $result); + if($count != 0) { + Log 2, "LUXTRONIK2_GetStatus: $name $host ".length($result)." -> ".$count; + return "0 != 0"; + } + + $socket->recv($result,4); + $count = unpack("N", $result); + if($count == 0) { + Log 2, "LUXTRONIK2_GetStatus: $name $host 0 Paramters read".length($result)." -> ".$count; + return "0 Paramters read"; + } + + $socket->recv($result, $count*4+4); + if(length($result) != $count*4) { + Log 1, "LUXTRONIK2_GetStatus status report length check: $name $host ".length($result)." should have been ". $count * 4; + return "Value read mismatch Lux2 ( $!)\n"; + } + @heatpump_values = unpack("N!$count", $result); + if(scalar(@heatpump_values) != $count) { + Log 2, "LUXTRONIK2_GetStatus10: $name $host ".scalar(@heatpump_values)." -> ".$heatpump_values[10]; + return "Value unpacking problem"; + } + + # Parametereinstellung lesen + $socket->send(pack("N", 3003)); + $socket->send(pack("N", 0)); + + $socket->recv($result,4); + $count = unpack("N", $result); + $count = unpack("N", $result); + if($count != 3003) { + Log 2, "LUXTRONIK2_GetStatus: $name $host 3003 Status problem 1: ".length($result)." -> ".$count; + return "3003 != 3003"; + } + + $socket->recv($result,4); + $count = unpack("N", $result); + + $socket->recv($result, $count*4+4); + if(length($result) != $count*4) { + my $loop = 4; # safety net in case of communication problems + while((length($result) < ($count * 4)) && ($loop-- > 0) ) { + my $result2; + my $newcnt = ($count * 4) - length($result); + $socket->recv($result2, $newcnt); + $result .= $result2; +# Log 3, "LUXTRONIK2_GetStatus read additional " . length($result2) +# . " bytes of expected " . $newcnt . " bytes, total should be " +# . $count * 4 . " buflen=" . length($result); + } + if($loop == 0) { + Log 3, "LUXTRONIK2_GetStatus parameter settings length check: $name $host " + . length($result) . " should have been " . $count * 4; + } + } + @heatpump_parameters = unpack("N$count", $result); + if(scalar(@heatpump_parameters) != $count) { + Log 1, "LUXTRONIK2_GetStatus: $name $host pump parameter problem: received parameter count (" + . scalar(@heatpump_parameters) . + ") is not equal to announced parameter count(" . $count . ")!"; + return "Parameter read mismatch LUXTRONIK2 ( $!)\n"; + } + + $socket->close(); + + if($err_log ne "") + { + Log GetLogLevel($name,2), "LUXTRONIK2 ".$err_log; + return("LUXTRONIK2 general problem with heatpump connection"); + } + + my %wpOpStat1 = ( 0 => "Waermepumpe laeuft", + 1 => "Waermepumpe steht", + 2 => "Waermepumpe kommt", + 3 => "Fehler", + 4 => "Abtauen" ); + my %wpOpStat2 = ( 0 => "Heizbetrieb", + 1 => "Keine Anforderung", + 2 => "Netz Einschaltverzögerung", + 3 => "Schaltspielzeit", + 4 => "EVU Sperrzeit", + 5 => "Brauchwasser", + 6 => "Stufe", + 7 => "Abtauen", + 8 => "Pumpenvorlauf", + 9 => "Thermische Desinfektion", + 10 => "Kühlbetrieb", + 12 => "Schwimmbad", + 13 => "Heizen_Ext_En", + 14 => "Brauchw_Ext_En", + 16 => "Durchflussueberwachung", + 17 => "Elektrische Zusatzheizung" ); + my %wpMode = ( 0 => "Automatik", + 1 => "Zusatzheizung", + 2 => "Party", + 3 => "Ferien", + 4 => "Aus" ); + + # Erst die operativen Stati und Parameterenstellungen + + $sensor = "firmware"; + $value = ''; + for(my $fi=81; $fi<91; $fi++) { + $value .= chr($heatpump_values[$fi]) if $heatpump_values[$fi]; + } + if($hash->{READINGS}{$sensor}{VAL} ne $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + + $sensor = "currentOperatingStatus1"; + $switch = $heatpump_values[117]; + $value = $wpOpStat1{$switch}; + $value = "unbekannt (".$switch.")" unless $value; + + if($hash->{READINGS}{$sensor}{VAL} ne $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + + $state = $value; + + $sensor = "currentOperatingStatus2"; + $switch = $heatpump_values[119]; + $value = $wpOpStat2{$switch}; + + # Sonderfaelle behandeln: + if ($switch==6) { $value = "Stufe ".$heatpump_values[121]." ".($heatpump_values[122] / 10)." °C "; } + elsif ($switch==7) { + if ($heatpump_values[44]==1) {$value = "Abtauen (Kreisumkehr)";} + else {$value = "Luftabtauen";} + } + $value = "unbekannt (".$switch.")" unless $value; + + if($hash->{READINGS}{$sensor}{VAL} ne $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + + $state = $state." - ".$value; + $hash->{READINGS}{state}{VAL} = $state; + $hash->{READINGS}{state}{TIME} = TimeNow(); + + $sensor = "hotWaterOperatingMode"; + $switch = $heatpump_parameters[4]; + + $value = $wpMode{$switch}; + $value = "unbekannt (".$switch.")" unless $value; + + if($hash->{READINGS}{$sensor}{VAL} ne $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + + $sensor = "heatingOperatingMode"; + $switch = $heatpump_parameters[3]; + + $value = $wpMode{$switch}; + if ($switch == 0 + && $heatpump_values[16] >= $heatpump_parameters[700] + && $heatpump_parameters[699] == 1) + {$value = "Automatik - Sommerbetrieb (Aus)";} + $value = "unbekannt (" . $switch . ")" unless $value; + + if($hash->{READINGS}{$sensor}{VAL} ne $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + +##################### +# Jetzt die aktuellen Betriebswerte auswerten. +##################### + + # is ambient temperature the correct wording for the outside temperature? + # Wikipedia: + # Ambient temperature simply means "the temperature of the surroundings" and will be the same as room temperature indoors. + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[15],"ambientTemperature"); + + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[16],"averageAmbientTemperature"); + + Log GetLogLevel($name,4), $sensor.": ".$value; +# Log 4, "LUXTRONIK2_GetStatus: $name $host ".$hash->{STATE}." -> ".$state; + + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[17],"hotWaterTemperature"); + + # Wert 10 gibt die Vorlauftemperatur an, die + # korrekte Uebersetzung ist flow temperature. + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[10],"flowTemperature"); + # Ruecklauftempereatur + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[11],"returnTemperature"); + # Ruecklauftemperatur Sollwert + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[12],"returnTemperatureTarget"); + # Ruecklauftemperatur am externen Sensor. + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[13],"returnTemperatureExtern"); + +# Wärmequellen + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[19],"heatSourceIN"); + LUXTRONIK2_TempValueMerken($hash,$heatpump_values[20],"heatSourceOUT"); + + + # Durchfluss Waermemengenzaehler + $sensor = "flowRate"; + $value = $heatpump_values[155]; + if($hash->{READINGS}{$sensor}{VAL} != $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{READINGS}{$sensor}{UNIT} = "l/h"; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + # Waermemengenzaehler + $sensor = "flowCountHeating"; + $value = $heatpump_values[151]; + if($hash->{READINGS}{$sensor}{VAL} != $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{READINGS}{$sensor}{UNIT} = "Wh"; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + # Waermemengenzaehler + $sensor = "flowCountHotWater"; + $value = $heatpump_values[152]; + if($hash->{READINGS}{$sensor}{VAL} != $value) { + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{READINGS}{$sensor}{UNIT} = "Wh"; + $hash->{CHANGED}[$cc++] = $sensor.": ".$value; + } + + if(AttrVal($hash->{NAME}, "statusHTML", "none") ne "none") { + $sensor = "floorplanHTML"; + $value = '
' . $name . "
"; + $value .= $hash->{READINGS}{'currentOperatingStatus1'}{VAL} . '
'; + $value .= $hash->{READINGS}{'currentOperatingStatus2'}{VAL} . '
'; + $value .= "Brauchwasser:" . $hash->{READINGS}{hotWaterTemperature}{VAL} . '°C'; + $hash->{READINGS}{$sensor}{TIME} = TimeNow(); + $hash->{READINGS}{$sensor}{VAL} = $value; + $hash->{READINGS}{$sensor}{UNIT} = "HTML"; + } + + DoTrigger($name, undef) if($init_done); +} + +1; + +=pod +=begin html + + +

LUXTRONIK2

+
    + Luxtronik 2.0 is a heating controller used in Alpha Innotec and Siemens Novelan Heatpumps. + It has a builtin Ethernet Port, so it can be directly integrated into a local area network. +
    + + + Define +
      + define <name> LUXTRONIK2 <IP-address> [<poll-interval>] +
      + If the pool interval is omitted, it is set to 300 (seconds). +
      + Example: +
        + define Heizung LUXTRONIK2 192.168.0.12 600 +
      +
    +
    + + + Set +
      + Nothing to set here yet... +
    +
    + + + Get +
      + No get implemented yet ... +
    +
    + + + Attributes +
      +
    • statusHTML
      + if set, creates a HTML-formatted reading named "floorplanHTML" for use with the FLOORPLAN module.
      + Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heatpump and the temperature of the water.
    • +
    • do_not_notify
    • +
    • loglevel
    • +
    +
    + +
+ +=end html +=cut diff --git a/contrib/23_LUXTRONIK2.pm b/contrib/23_LUXTRONIK2.pm index caac6ad4f..6c6b592fe 100644 --- a/contrib/23_LUXTRONIK2.pm +++ b/contrib/23_LUXTRONIK2.pm @@ -3,7 +3,7 @@ # Copyright notice # # (c) 2012 Torsten Poitzsch (torsten.poitzsch@gmx.de) -# (c) 2012 Jan-Hinrich Fessel (oskar@fessel.org) +# (c) 2012-2013 Jan-Hinrich Fessel (oskar@fessel.org) # # This script is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -381,46 +381,49 @@ LUXTRONIK2_GetStatus($)

LUXTRONIK2

    Luxtronik 2.0 is a heating controller used in Alpha Innotec and Siemens Novelan Heatpumps. - It can be directly integrated into a local area network (Ethernet port).
    + It has a builtin Ethernet Port, so it can be directly integrated into a local area network.
    - + Define
      define <name> LUXTRONIK2 <IP-address> [<poll-interval>] -

      - If the pool interval is omitted, it is set to 300 (seconds). -

      +
      + If the pool interval is omitted, it is set to 300 (seconds). +
      Example:
        - define Heizung LUXTRONIK2 192.168.0.12 600
        + define Heizung LUXTRONIK2 192.168.0.12 600

    - + Set -
      +
        Nothing to set here yet... -
      +

    - + Get
      - No get implemented yet ... + No get implemented yet ...

    - + Attributes
      +
    • statusHTML
      + if set, creates a HTML-formatted reading named "floorplanHTML" for use with the FLOORPLAN module.
      + Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heatpump and the temperature of the water.
    • do_not_notify
    • loglevel

    - -
+ + =end html =cut diff --git a/www/gplot/LUX2.gplot b/www/gplot/LUX2.gplot new file mode 100644 index 000000000..3b1697c10 --- /dev/null +++ b/www/gplot/LUX2.gplot @@ -0,0 +1,36 @@ +############################ +# Display the measured temp and the actuator. +# Corresponding FileLog definition: +# define FileLog /var/log/fhem/fht-%Y.log + +set terminal png transparent size crop +set output '.png' +set xdata time +set timefmt "%Y-%m-%d_%H:%M:%S" +set xlabel " " +set ytics nomirror +set y2tics +#set ytics +set title '' +set grid xtics y2tics + +set y2label "Temperature in C" +set ylabel "Temperature in C" + +#FileLog 4:flowTemperature:0: +#FileLog 4:returnTemperatureTarget:0: +#FileLog 4:returnTemperature:0: +#FileLog 4:ambientTemperature:0: +#FileLog 4:averageAmbientTemperature:0: + +plot \ + "< awk '/flowTemperature/{print $1, $4}' "\ + using 1:2 axes x1y1 title 'Vorlauftemperatur' with lines,\ + "< awk '/returnTemperatureTarget/{print $1, $4}' "\ + using 1:2 axes x1y1 title 'Rücklauftemperatur SOLL' with lines,\ + "< awk '/returnTemperature/ {print $1, $4+0}' "\ + using 1:2 axes x1y1 title 'Rücklauftemperatur' with lines,\ + "< awk '/ambientTemperature/ {print $1, $4+0}' "\ + using 1:2 axes x1y1 title 'Außentemperatur' with lines lw2,\ + "< awk '/averageAmbient/ {print $1, $4+0}' "\ + using 1:2 axes x1y1 title 'durchschnittliche Außentemperatur' with lines lw2\