2013-01-01 14:21:40 +00:00
|
|
|
################################################################
|
|
|
|
#
|
|
|
|
# Copyright notice
|
|
|
|
#
|
2014-01-16 18:17:49 +00:00
|
|
|
# (c) 2012,2014 Torsten Poitzsch (torsten.poitzsch@gmx.de)
|
2014-01-18 15:12:15 +00:00
|
|
|
# (c) 2012-2013 Jan-Hinrich Fessel (oskar@fessel.org)
|
2013-01-01 14:21:40 +00:00
|
|
|
#
|
|
|
|
# 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;
|
2014-01-16 18:17:49 +00:00
|
|
|
use Blocking;
|
2013-01-01 14:21:40 +00:00
|
|
|
use IO::Socket;
|
2014-01-18 15:12:15 +00:00
|
|
|
use Time::HiRes qw/ time /;
|
|
|
|
use POSIX;
|
2013-01-01 14:21:40 +00:00
|
|
|
|
|
|
|
my $cc; # The Itmes Changed Counter
|
|
|
|
|
|
|
|
sub
|
|
|
|
LUXTRONIK2_Initialize($)
|
|
|
|
{
|
|
|
|
my ($hash) = @_;
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
$hash->{DefFn} = "LUXTRONIK2_Define";
|
|
|
|
$hash->{UndefFn} = "LUXTRONIK2_Undefine";
|
|
|
|
$hash->{SetFn} = "LUXTRONIK2_Set";
|
|
|
|
$hash->{AttrList} = "disable:0,1 ".
|
2014-01-18 15:12:15 +00:00
|
|
|
"allowSetParameter ".
|
2014-01-16 18:17:49 +00:00
|
|
|
"statusHTML ".
|
|
|
|
$readingFnAttributes;
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub
|
|
|
|
LUXTRONIK2_Define($$)
|
|
|
|
{
|
|
|
|
my ($hash, $def) = @_;
|
|
|
|
my @a = split("[ \t][ \t]*", $def);
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
return "Usage: define <name> LUXTRONIK2 <ip-address> [poll-interval]" if(@a <3 || @a >4);
|
|
|
|
|
|
|
|
my $name = $a[0];
|
|
|
|
my $host = $a[2];
|
|
|
|
|
|
|
|
my $interval = 5*60;
|
|
|
|
$interval = $a[3] if(int(@a) == 4);
|
|
|
|
$interval = 1*60 if( $interval < 1*60 );
|
|
|
|
|
|
|
|
$hash->{NAME} = $name;
|
|
|
|
|
|
|
|
$hash->{STATE} = "Initializing";
|
|
|
|
$hash->{HOST} = $host;
|
|
|
|
$hash->{INTERVAL} = $interval;
|
|
|
|
|
|
|
|
RemoveInternalTimer($hash);
|
|
|
|
#Get first data after 10 seconds
|
|
|
|
InternalTimer(gettimeofday() + 10, "LUXTRONIK2_GetUpdate", $hash, 0);
|
2014-01-18 15:12:15 +00:00
|
|
|
|
|
|
|
#Reset temporary values (min and max reading durations)
|
|
|
|
$hash->{fhem}{durationFetchReadingsMin} = 0;
|
|
|
|
$hash->{fhem}{durationFetchReadingsMax} = 0;
|
2013-01-01 14:21:40 +00:00
|
|
|
|
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub
|
2014-01-16 18:17:49 +00:00
|
|
|
LUXTRONIK2_Undefine($$)
|
2013-01-01 14:21:40 +00:00
|
|
|
{
|
2014-01-16 18:17:49 +00:00
|
|
|
my ($hash, $arg) = @_;
|
|
|
|
|
|
|
|
RemoveInternalTimer($hash);
|
|
|
|
|
|
|
|
BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
|
|
|
|
|
|
|
|
return undef;
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
sub
|
|
|
|
LUXTRONIK2_Set($$@)
|
|
|
|
{
|
2014-01-18 15:12:15 +00:00
|
|
|
my ($hash, $name, $cmd, $val) = @_;
|
|
|
|
my $resultStr;
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
if($cmd eq 'statusRequest') {
|
|
|
|
$hash->{LOCAL} = 1;
|
|
|
|
LUXTRONIK2_GetUpdate($hash);
|
|
|
|
$hash->{LOCAL} = 0;
|
|
|
|
return undef;
|
|
|
|
}
|
2014-01-18 15:12:15 +00:00
|
|
|
elsif($cmd eq 'INTERVAL' && int(@_)==4 ) {
|
|
|
|
$val = 1*60 if( $val < 1*60 );
|
|
|
|
$hash->{INTERVAL}=$val;
|
|
|
|
return "Polling interval set to $val seconds.";
|
|
|
|
}
|
|
|
|
elsif(int(@_)==4 && (
|
|
|
|
$cmd eq 'hotWaterTemperatureTarget' ||
|
|
|
|
$cmd eq 'hotWaterOperatingMode'
|
|
|
|
)) {
|
|
|
|
$hash->{LOCAL} = 1;
|
|
|
|
$resultStr = LUXTRONIK2_SetParameter ($hash, $cmd, $val);
|
|
|
|
$hash->{LOCAL} = 0;
|
|
|
|
return $resultStr;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $list = "statusRequest:noArg".
|
|
|
|
" hotWaterTemperatureTarget:slider,30.0,0.5,65.0".
|
|
|
|
" hotWaterOperatingMode:Automatik,Party,Off".
|
|
|
|
" INTERVAL:slider,60,30,1800";
|
2014-01-16 18:17:49 +00:00
|
|
|
return "Unknown argument $cmd, choose one of $list";
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
2013-01-01 14:21:40 +00:00
|
|
|
sub
|
2014-01-16 18:17:49 +00:00
|
|
|
LUXTRONIK2_GetUpdate($)
|
2013-01-01 14:21:40 +00:00
|
|
|
{
|
|
|
|
my ($hash) = @_;
|
|
|
|
my $name = $hash->{NAME};
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
if(!$hash->{LOCAL}) {
|
|
|
|
RemoveInternalTimer($hash);
|
|
|
|
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "LUXTRONIK2_GetUpdate", $hash, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
my $host = $hash->{HOST};
|
|
|
|
|
|
|
|
if( !$hash->{LOCAL} ) {
|
|
|
|
return undef if( AttrVal($name, "disable", 0 ) == 1 );
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
$hash->{helper}{RUNNING_PID} = BlockingCall("LUXTRONIK2_DoUpdate", $name."|".$host, "LUXTRONIK2_UpdateDone", 10, "LUXTRONIK2_UpdateAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
|
|
|
|
}
|
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
sub
|
|
|
|
LUXTRONIK2_DoUpdate($)
|
|
|
|
{
|
|
|
|
my ($string) = @_;
|
|
|
|
my ($name, $host) = split("\\|", $string);
|
|
|
|
|
|
|
|
my @heatpump_values;
|
|
|
|
my @heatpump_parameters;
|
|
|
|
my $count=0;
|
|
|
|
my $result="";
|
|
|
|
my $readingStartTime = time();
|
|
|
|
|
|
|
|
Log3 $name, 5, "$name: Opening connection to host ".$host;
|
2013-01-01 14:21:40 +00:00
|
|
|
my $socket = new IO::Socket::INET ( PeerAddr => $host,
|
|
|
|
PeerPort => 8888,
|
|
|
|
# Type => SOCK_STREAM, # probably needed on some systems
|
|
|
|
Proto => 'tcp'
|
|
|
|
);
|
|
|
|
if (!$socket) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 1, "$name Error: Could not open connection to host ".$host;
|
|
|
|
return "$name|0|Can't connect to $host";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
$socket->autoflush(1);
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
############################
|
|
|
|
#Fetch operational values (FOV)
|
|
|
|
############################
|
|
|
|
Log3 $name, 5, "$name: Ask host for operational values";
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->send(pack("N", 3004));
|
|
|
|
$socket->send(pack("N", 0));
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 5, "$name: Start to receive operational values";
|
|
|
|
#(FOV) read first 4 digits of response -> should be request_echo = 3004
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->recv($result,4);
|
|
|
|
$count = unpack("N", $result);
|
|
|
|
if($count != 3004) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: Fetching operational values - wrong echo of request 3004: ".length($result)." -> ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|3004 != 3004";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
#(FOV) read next 4 digits of response -> should be status = 0
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->recv($result,4);
|
|
|
|
$count = unpack("N", $result);
|
2014-01-16 18:17:49 +00:00
|
|
|
if($count > 0) {
|
|
|
|
Log3 $name, 4, "$name parameter on target changed, restart parameter reading after 5 seconds";
|
|
|
|
$socket->close();
|
|
|
|
return "$name|2|Status = $count - parameter on target changed, restart device reading after 5 seconds";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
#(FOV) read next 4 digits of response -> should be number_of_parameters > 0
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->recv($result,4);
|
|
|
|
$count = unpack("N", $result);
|
|
|
|
if($count == 0) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: Fetching operational values - 0 values announced: ".length($result)." -> ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|0 values read";
|
|
|
|
}
|
|
|
|
|
|
|
|
#(FOV) read remaining response -> should be previous number of parameters
|
|
|
|
my $i=1;
|
|
|
|
$result="";
|
|
|
|
my $buf="";
|
|
|
|
while($i<=$count) {
|
|
|
|
$socket->recv($buf,4);
|
|
|
|
$result.=$buf;
|
|
|
|
$i++;
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
if(length($result) != $count*4) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 1, "$name LUXTRONIK2_DoUpdate-Error: operational values length check: ".length($result)." should have been ". $count * 4;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|Number of values read mismatch ( $!)\n";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
|
|
|
|
#(FOV) unpack response in array
|
|
|
|
@heatpump_values = unpack("N$count", $result);
|
2013-01-01 14:21:40 +00:00
|
|
|
if(scalar(@heatpump_values) != $count) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: unpacking problem by operation values: ".scalar(@heatpump_values)." instead of ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|Unpacking problem of operational values";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
|
|
|
|
Log3 $name, 5, "$name: $count operational values received";
|
|
|
|
|
|
|
|
############################
|
|
|
|
#Fetch set parameters (FSP)
|
|
|
|
############################
|
|
|
|
Log3 $name, 5, "$name: Ask host for set parameters";
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->send(pack("N", 3003));
|
|
|
|
$socket->send(pack("N", 0));
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 5, "$name: Start to receive set parameters";
|
|
|
|
#(FSP) read first 4 digits of response -> should be request_echo=3003
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->recv($result,4);
|
|
|
|
$count = unpack("N", $result);
|
|
|
|
if($count != 3003) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: wrong echo of request 3003: ".length($result)." -> ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|3003 != 3003";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
#(FSP) read next 4 digits of response -> should be number_of_parameters > 0
|
2013-01-01 14:21:40 +00:00
|
|
|
$socket->recv($result,4);
|
|
|
|
$count = unpack("N", $result);
|
2014-01-16 18:17:49 +00:00
|
|
|
if($count == 0) {
|
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: 0 parameter read: ".length($result)." -> ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|0 parameter read";
|
|
|
|
}
|
|
|
|
|
|
|
|
#(FSP) read remaining response -> should be previous number of parameters
|
|
|
|
my $i=1;
|
|
|
|
$result="";
|
|
|
|
my $buf="";
|
|
|
|
while($i<=$count) {
|
|
|
|
$socket->recv($buf,4);
|
|
|
|
$result.=$buf;
|
|
|
|
$i++;
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
if(length($result) != $count*4) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 1, "$name LUXTRONIK2_DoUpdate-Error: parameter length check: ".length($result)." should have been ". $count * 4;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|Number of parameters read mismatch ( $!)\n";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
|
2013-01-01 14:21:40 +00:00
|
|
|
@heatpump_parameters = unpack("N$count", $result);
|
|
|
|
if(scalar(@heatpump_parameters) != $count) {
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: unpacking problem by set parameter: ".scalar(@heatpump_parameters)." instead of ".$count;
|
|
|
|
$socket->close();
|
|
|
|
return "$name|0|Unpacking problem of set parameters";
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 5, "$name: $count set values received";
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $name, 5, "$name: Closing connection to host $host";
|
|
|
|
$socket->close();
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
my $readingEndTime = time();
|
|
|
|
|
|
|
|
#return certain readings for further processing
|
|
|
|
# 0 - name
|
|
|
|
my $return_str="$name";
|
|
|
|
# 1 - no error = 1
|
|
|
|
$return_str .= "|1";
|
|
|
|
# 2 - currentOperatingStatus1
|
|
|
|
$return_str .= "|".$heatpump_values[117];
|
|
|
|
# 3 - currentOperatingStatus2
|
|
|
|
$return_str .= "|".$heatpump_values[119];
|
|
|
|
# 4 - Stufe - Value 121
|
|
|
|
$return_str .= "|".$heatpump_values[121];
|
|
|
|
# 5 - Temperature Value 122
|
|
|
|
$return_str .= "|".$heatpump_values[122];
|
|
|
|
# 6 - Kreisumkehrventil
|
|
|
|
$return_str .= "|".$heatpump_values[44];
|
|
|
|
# 7 - hotWaterOperatingMode
|
|
|
|
$return_str .= "|".$heatpump_parameters[4];
|
|
|
|
# 8 - hotWaterMonitoring
|
|
|
|
$return_str .= "|".$heatpump_values[124];
|
|
|
|
# 9 - hotWaterBoilerValve
|
|
|
|
$return_str .= "|".$heatpump_values[38];
|
|
|
|
# 10 - heatingOperatingMode
|
|
|
|
$return_str .= "|".$heatpump_parameters[3];
|
|
|
|
# 11 - heatingSommerMode
|
|
|
|
$return_str .= "|".$heatpump_parameters[699];
|
|
|
|
# 12 - ambientTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[15];
|
|
|
|
# 13 - averageAmbientTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[16];
|
|
|
|
# 14 - hotWaterTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[17];
|
|
|
|
# 15 - flowTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[10];
|
|
|
|
# 16 - returnTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[11];
|
|
|
|
# 17 - returnTemperatureTarget
|
|
|
|
$return_str .= "|".$heatpump_values[12];
|
|
|
|
# 18 - returnTemperatureExtern
|
|
|
|
$return_str .= "|".$heatpump_values[13];
|
|
|
|
# 19 - flowRate
|
|
|
|
$return_str .= "|".$heatpump_values[155];
|
|
|
|
# 20 - firmware
|
|
|
|
my $fwvalue = "";
|
2013-01-01 14:21:40 +00:00
|
|
|
for(my $fi=81; $fi<91; $fi++) {
|
2014-01-16 18:17:49 +00:00
|
|
|
$fwvalue .= chr($heatpump_values[$fi]) if $heatpump_values[$fi];
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
$return_str .= "|".$fwvalue;
|
|
|
|
# 21 - thresholdTemperatureSummerMode
|
|
|
|
$return_str .= "|".$heatpump_parameters[700];
|
|
|
|
# 22 - readingsDeviceTime
|
|
|
|
$return_str .= "|".$heatpump_values[134];
|
|
|
|
# 23 - heatSourceIN
|
|
|
|
$return_str .= "|".$heatpump_values[19];
|
|
|
|
# 24 - heatSourceOUT
|
|
|
|
$return_str .= "|".$heatpump_values[20];
|
|
|
|
# 25 - hotWaterTemperatureTarget
|
|
|
|
$return_str .= "|".$heatpump_values[18];
|
|
|
|
# 26 - hotGasTemperature
|
|
|
|
$return_str .= "|".$heatpump_values[14];
|
|
|
|
# 27 - heatingSystemCirculationPump
|
|
|
|
$return_str .= "|".$heatpump_values[39];
|
|
|
|
# 28 - hotWaterCirculatingPumpExtern
|
|
|
|
$return_str .= "|".$heatpump_values[46];
|
|
|
|
# 29 - readingStartTime
|
|
|
|
$return_str .= "|".$readingStartTime;
|
|
|
|
# 30 - readingEndTime
|
|
|
|
$return_str .= "|".$readingEndTime;
|
|
|
|
|
|
|
|
return $return_str;
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
sub
|
|
|
|
LUXTRONIK2_UpdateDone($)
|
|
|
|
{
|
|
|
|
my ($string) = @_;
|
|
|
|
my $value = "";
|
|
|
|
my $state = "";
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
return unless(defined($string));
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
my @a = split("\\|",$string);
|
|
|
|
my $hash = $defs{$a[0]};
|
|
|
|
my $name = $a[0];
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
delete($hash->{helper}{RUNNING_PID});
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
return if($hash->{helper}{DISABLED});
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
Log3 $hash, 5, "$name: LUXTRONIK2_UpdateDone: $string";
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
#Define Status Messages
|
|
|
|
my %wpOpStat1 = ( 0 => "Waermepumpe laeuft",
|
|
|
|
1 => "Waermepumpe steht",
|
|
|
|
2 => "Waermepumpe kommt",
|
|
|
|
4 => "Fehler",
|
|
|
|
5 => "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 => "Kuehlbetrieb",
|
|
|
|
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" );
|
|
|
|
#List of firmware that are known to be compatible with this modul
|
2014-01-18 15:12:15 +00:00
|
|
|
my $compatibleFirmware = "#V1.54C#";
|
2014-01-16 18:17:49 +00:00
|
|
|
|
|
|
|
my $counterRetry = $hash->{fhem}{counterRetry};
|
|
|
|
$counterRetry++;
|
|
|
|
|
|
|
|
if ($a[1]==0 ) {
|
|
|
|
readingsSingleUpdate($hash,"state","Error: ".$a[2],1);
|
|
|
|
$counterRetry = 0;
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
elsif ($a[1]==2 ) {
|
|
|
|
if ($counterRetry <=3) {
|
|
|
|
InternalTimer(gettimeofday() + 5, "LUXTRONIK2_GetUpdate", $hash, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
readingsSingleUpdate($hash,"state","Error: Reading skipped after $counterRetry tries",1);
|
|
|
|
Log3 $hash, 2, "$name Error: Device reading skipped after $counterRetry tries with parameter change on target";
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
elsif ($a[1]==1 ) {
|
|
|
|
$counterRetry = 0;
|
|
|
|
readingsBeginUpdate($hash);
|
|
|
|
|
|
|
|
#Operating status of heatpump
|
|
|
|
my $currentOperatingStatus1 = $wpOpStat1{$a[2]};
|
|
|
|
$currentOperatingStatus1 = "unbekannt (".$a[2].")" unless $currentOperatingStatus1;
|
|
|
|
readingsBulkUpdate($hash,"currentOperatingStatus1",$currentOperatingStatus1);
|
|
|
|
my $currentOperatingStatus2 = $wpOpStat2{$a[3]};
|
|
|
|
# refine text of second state
|
|
|
|
if ($a[3]==6) {
|
|
|
|
$currentOperatingStatus2 = "Stufe ".$a[4]." ".LUXTRONIK2_CalcTemp($a[5])." °C ";
|
|
|
|
}
|
|
|
|
elsif ($a[3]==7) {
|
|
|
|
if ($a[6]==1) {$currentOperatingStatus2 = "Abtauen (Kreisumkehr)";}
|
|
|
|
else {$currentOperatingStatus2 = "Luftabtauen";}
|
|
|
|
}
|
|
|
|
$currentOperatingStatus2 = "unbekannt (".$a[3].")" unless $currentOperatingStatus2;
|
|
|
|
readingsBulkUpdate($hash,"currentOperatingStatus2",$currentOperatingStatus2);
|
|
|
|
|
|
|
|
# Hot water operating mode
|
|
|
|
$value = $wpMode{$a[7]};
|
|
|
|
$value = "unbekannt (".$a[7].")" unless $value;
|
|
|
|
readingsBulkUpdate($hash,"hotWaterOperatingMode",$value);
|
|
|
|
# hotWaterStatus
|
2014-01-18 15:12:15 +00:00
|
|
|
if ($a[8]==0) {$value="Sperrzeit";}
|
2014-01-16 18:17:49 +00:00
|
|
|
elsif ($a[8]==1 && $a[9]==1) {$value="Aufheizen";}
|
|
|
|
elsif ($a[8]==1 && $a[9]==0) {$value="Temp.OK";}
|
2014-01-18 15:12:15 +00:00
|
|
|
elsif ($a[8]==3) {$value="Aus";}
|
2014-01-16 18:17:49 +00:00
|
|
|
else {$value = "unbekannt (".$a[8]."/".$a[9].")";}
|
|
|
|
readingsBulkUpdate($hash,"hotWaterStatus",$value);
|
|
|
|
|
|
|
|
# Heating operating mode including summer mode and average ambient temperature
|
|
|
|
readingsBulkUpdate($hash,"heatingSummerMode",$a[11]?"on":"off");
|
|
|
|
my $thresholdTemperatureSummerMode=LUXTRONIK2_CalcTemp($a[21]);
|
|
|
|
readingsBulkUpdate($hash,"thresholdTemperatureSummerMode",$thresholdTemperatureSummerMode);
|
|
|
|
my $averageAmbientTemperature=LUXTRONIK2_CalcTemp($a[13]);
|
|
|
|
readingsBulkUpdate($hash,"averageAmbientTemperature",$averageAmbientTemperature);
|
|
|
|
# Heating operating mode
|
|
|
|
$value = $wpMode{$a[10]};
|
|
|
|
# Consider also summer mode
|
|
|
|
if ($a[10] == 0
|
|
|
|
&& $a[11] == 1
|
|
|
|
&& $averageAmbientTemperature >= $thresholdTemperatureSummerMode)
|
|
|
|
{$value = "Automatik - Sommerbetrieb (Aus)";}
|
|
|
|
$value = "unbekannt (".$a[10].")" unless $value;
|
|
|
|
readingsBulkUpdate($hash,"heatingOperatingMode",$value);
|
|
|
|
|
|
|
|
# Remaining temperatures and flow rate
|
|
|
|
readingsBulkUpdate($hash,"ambientTemperature",LUXTRONIK2_CalcTemp($a[12]));
|
|
|
|
my $hotWaterTemperature = LUXTRONIK2_CalcTemp($a[14]);
|
|
|
|
readingsBulkUpdate($hash,"hotWaterTemperature",$hotWaterTemperature);
|
|
|
|
readingsBulkUpdate($hash,"hotWaterTemperatureTarget",LUXTRONIK2_CalcTemp($a[25]));
|
|
|
|
readingsBulkUpdate($hash,"flowTemperature",LUXTRONIK2_CalcTemp($a[15]));
|
|
|
|
readingsBulkUpdate($hash,"returnTemperature",LUXTRONIK2_CalcTemp($a[16]));
|
|
|
|
readingsBulkUpdate($hash,"returnTemperatureTarget",LUXTRONIK2_CalcTemp($a[17]));
|
|
|
|
readingsBulkUpdate($hash,"returnTemperatureExtern",LUXTRONIK2_CalcTemp($a[18]));
|
|
|
|
readingsBulkUpdate($hash,"flowRate",$a[19]);
|
|
|
|
readingsBulkUpdate($hash,"heatSourceIN",LUXTRONIK2_CalcTemp($a[23]));
|
|
|
|
readingsBulkUpdate($hash,"heatSourceOUT",LUXTRONIK2_CalcTemp($a[24]));
|
|
|
|
readingsBulkUpdate($hash,"hotGasTemperature",LUXTRONIK2_CalcTemp($a[26]));
|
|
|
|
|
|
|
|
# Input / Output status
|
|
|
|
readingsBulkUpdate($hash,"heatingSystemCirculationPump",$a[27]?"on":"off");
|
|
|
|
readingsBulkUpdate($hash,"hotWaterCirculationPumpExtern",$a[28]?"on":"off");
|
|
|
|
readingsBulkUpdate($hash,"hotWaterSwitchingValve",$a[9]?"on":"off");
|
|
|
|
|
|
|
|
# Firmware
|
|
|
|
readingsBulkUpdate($hash,"firmware",$a[20]);
|
|
|
|
# if unknown firmware, ask at each startup to inform comunity
|
|
|
|
if (!$hash->{fhem}{alertFirmware} && index("#".$a[20]."#",$compatibleFirmware) == -1) {
|
|
|
|
$hash->{fhem}{alertFirmware} = 1;
|
|
|
|
Log3 $hash, 2, "$name Alert: Host uses untested Firmware $a[20]. Please inform FHEM comunity about compatibility.";
|
|
|
|
}
|
|
|
|
|
|
|
|
# Device times during readings
|
|
|
|
$value = strftime "%Y-%m-%d %H:%M:%S", localtime($a[22]);
|
|
|
|
readingsBulkUpdate($hash, "deviceTimeStartReadings", $value);
|
2014-01-18 15:12:15 +00:00
|
|
|
readingsBulkUpdate($hash, "delayDeviceTime", floor($a[29]-$a[22]+0.5));
|
|
|
|
my $durationFetchReadings = floor(($a[30]-$a[29]+0.005)*100)/100;
|
|
|
|
readingsBulkUpdate($hash, "durationFetchReadings", $durationFetchReadings);
|
|
|
|
#Remember min and max reading durations, will be reset when initializing the device
|
|
|
|
if ($hash->{fhem}{durationFetchReadingsMin} == 0 || $hash->{fhem}{durationFetchReadingsMin} > $durationFetchReadings) {
|
|
|
|
$hash->{fhem}{durationFetchReadingsMin} = $durationFetchReadings;
|
|
|
|
}
|
|
|
|
if ($hash->{fhem}{durationFetchReadingsMax} < $durationFetchReadings) {
|
|
|
|
$hash->{fhem}{durationFetchReadingsMax} = $durationFetchReadings;
|
|
|
|
}
|
2014-01-16 18:17:49 +00:00
|
|
|
#HTML for floorplan
|
|
|
|
if(AttrVal($name, "statusHTML", "none") ne "none") {
|
|
|
|
$value = "<div class=fp_" . $a[0] . "_title>" . $a[0] . "</div>";
|
|
|
|
$value .= $currentOperatingStatus1 . "<br>";
|
|
|
|
$value .= $currentOperatingStatus2 . "<br>";
|
|
|
|
$value .= "Brauchwasser: " . $hotWaterTemperature . "°C";
|
|
|
|
readingsBulkUpdate($hash,"floorplanHTML",$value);
|
|
|
|
}
|
|
|
|
|
|
|
|
readingsBulkUpdate($hash,"state",$currentOperatingStatus1." - ".$currentOperatingStatus2);
|
|
|
|
|
|
|
|
readingsEndUpdate($hash,1);
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Log3 $hash, 5, "$name LUXTRONIK2_DoUpdate-Error: Status = $a[1]";
|
|
|
|
}
|
|
|
|
$hash->{fhem}{counterRetry} = $counterRetry;
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
}
|
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
sub
|
|
|
|
LUXTRONIK2_UpdateAborted($)
|
|
|
|
{
|
|
|
|
my ($hash) = @_;
|
|
|
|
delete($hash->{helper}{RUNNING_PID});
|
|
|
|
my $name = $hash->{NAME};
|
|
|
|
Log3 $hash, 1, "$name LUXTRONIK2_UpdateAborted: Timeout when connecting to host";
|
|
|
|
}
|
2013-01-01 14:21:40 +00:00
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
2014-01-16 18:17:49 +00:00
|
|
|
sub
|
|
|
|
LUXTRONIK2_CalcTemp($)
|
|
|
|
{
|
|
|
|
my ($temp) = @_;
|
|
|
|
if ($temp > 100000) {$temp = $temp-4294967296;}
|
|
|
|
$temp /= 10;
|
|
|
|
return $temp;
|
2013-01-01 14:21:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-18 15:12:15 +00:00
|
|
|
|
|
|
|
sub
|
|
|
|
LUXTRONIK2_SetParameter($$$)
|
|
|
|
{
|
|
|
|
my ($hash, $parameterName, $realValue) = @_;
|
|
|
|
my $setParameter = 0;
|
|
|
|
my $setValue = 0;
|
|
|
|
my $result;
|
|
|
|
my $buffer;
|
|
|
|
my $host = $hash->{HOST};
|
|
|
|
my $name = $hash->{NAME};
|
|
|
|
|
|
|
|
my %opMode = ( "Automatik" => 0,
|
|
|
|
"Party" => 2,
|
|
|
|
"Off" => 4);
|
|
|
|
|
|
|
|
if(AttrVal($name, "allowSetParameter", "no") ne "yes") {
|
|
|
|
return $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 'yes'";
|
|
|
|
}
|
|
|
|
if ($parameterName eq "hotWaterTemperatureTarget") {
|
|
|
|
#parameter number
|
|
|
|
$setParameter = 2;
|
|
|
|
#limit temperature range
|
|
|
|
$realValue = 30 if( $realValue < 30 );
|
|
|
|
$realValue = 65 if( $realValue > 65 );
|
|
|
|
#Allow only integer temperature or with decimal .5
|
|
|
|
$setValue = int($realValue * 2) * 5;
|
|
|
|
$realValue = $setValue / 10;
|
|
|
|
}
|
|
|
|
elsif ($parameterName eq "hotWaterOperatingMode") {
|
|
|
|
if (! exists($opMode{$realValue})) {
|
|
|
|
return "$name Error: Wrong parameter given for hotWaterOperatingMode, use Automatik,Party,Off"
|
|
|
|
}
|
|
|
|
$setParameter = 4;
|
|
|
|
$setValue = $opMode{$realValue};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return "$name LUXTRONIK2_SetParameter-Error: unknown parameter $parameterName";
|
|
|
|
}
|
|
|
|
|
|
|
|
############################
|
|
|
|
# Send new parameter to host
|
|
|
|
############################
|
|
|
|
if ($setParameter !=0) {
|
|
|
|
Log3 $name, 5, "$name: Opening connection to host ".$host;
|
|
|
|
my $socket = new IO::Socket::INET ( PeerAddr => $host,
|
|
|
|
PeerPort => 8888,
|
|
|
|
Proto => 'tcp'
|
|
|
|
);
|
|
|
|
if (!$socket) {
|
|
|
|
Log3 $name, 1, "$name LUXTRONIK2_SetParameter-Error: Could not open connection to host ".$host;
|
|
|
|
return "$name Error: Could not open connection to host ".$host;
|
|
|
|
}
|
|
|
|
$socket->autoflush(1);
|
|
|
|
|
|
|
|
Log3 $name, 5, "$name: Set parameter $parameterName ($setParameter) = $realValue ($setValue)";
|
|
|
|
$socket->send(pack("N", 3002));
|
|
|
|
$socket->send(pack("N", $setParameter));
|
|
|
|
$socket->send(pack("N", $setValue));
|
|
|
|
|
|
|
|
Log3 $name, 5, "$name: Receive confirmation";
|
|
|
|
#read first 4 digits of response -> should be request_echo = 3002
|
|
|
|
$socket->recv($buffer,4);
|
|
|
|
$result = unpack("N", $buffer);
|
|
|
|
if($result != 3002) {
|
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_SetParameter-Error: Set parameter $parameterName - wrong echo of request: $result instead of 3002";
|
|
|
|
$socket->close();
|
|
|
|
return "$name Error: Host did not confirm parameter setting";
|
|
|
|
}
|
|
|
|
|
|
|
|
#Read next 4 digits of response -> should be setParameter
|
|
|
|
$socket->recv($buffer,4);
|
|
|
|
$result = unpack("N", $buffer);
|
|
|
|
if($result !=$setParameter) {
|
|
|
|
Log3 $name, 2, "$name LUXTRONIK2_SetParameter-Error: Set parameter $parameterName - missing confirmation: $result instead of $setParameter";
|
|
|
|
$socket->close();
|
|
|
|
return "$name Error: Host did not confirm parameter setting";
|
|
|
|
}
|
|
|
|
Log3 $name, 5, "$name: Parameter setting confirmed";
|
|
|
|
|
|
|
|
$socket->close();
|
|
|
|
|
|
|
|
readingsSingleUpdate($hash,$parameterName,$realValue,1);
|
|
|
|
|
|
|
|
return "$name: Parameter $parameterName set to $realValue";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-01 14:21:40 +00:00
|
|
|
1;
|
|
|
|
|
|
|
|
=pod
|
|
|
|
=begin html
|
|
|
|
|
|
|
|
<a name="LUXTRONIK2"></a>
|
|
|
|
<h3>LUXTRONIK2</h3>
|
|
|
|
<ul>
|
|
|
|
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.
|
2014-01-16 18:17:49 +00:00
|
|
|
<i>The modul uses the communication features of the firmware v1.54C.</i>
|
2013-01-01 14:21:40 +00:00
|
|
|
<br>
|
|
|
|
|
|
|
|
<a name="LUXTRONIK2define"></a>
|
|
|
|
<b>Define</b>
|
|
|
|
<ul>
|
2014-01-16 18:17:49 +00:00
|
|
|
<code>define <name> LUXTRONIK2 <IP-address> [poll-interval]</code>
|
2013-01-01 14:21:40 +00:00
|
|
|
<br>
|
2014-01-16 18:17:49 +00:00
|
|
|
If the pool interval is omitted, it is set to 300 (seconds). Smallest possible value is 60.
|
2013-01-01 14:21:40 +00:00
|
|
|
<br>
|
|
|
|
Example:
|
|
|
|
<ul>
|
|
|
|
<code>define Heizung LUXTRONIK2 192.168.0.12 600</code>
|
|
|
|
</ul>
|
|
|
|
</ul>
|
|
|
|
<br>
|
|
|
|
|
|
|
|
<a name="LUXTRONIK2set"></a>
|
|
|
|
<b>Set </b>
|
2014-01-18 15:12:15 +00:00
|
|
|
<ul><b><hotWaterOperatingMode%gt;</b> <Mode:Auto|Party|Off%gt;- Operating Mode of domestic hot water boiler</ul>
|
|
|
|
<ul><b><hotWaterTemperatureTarget%gt;</b> <temperature °C%gt; - Target temperature of domestic hot water boiler</ul>
|
|
|
|
<ul><b><INTERVAL%gt;</b> <seconds%gt; - Polling interval</ul>
|
|
|
|
<ul><b><statusRequest%gt;</b> - Update device information</ul>
|
2013-01-01 14:21:40 +00:00
|
|
|
<br>
|
|
|
|
|
|
|
|
<a name="LUXTRONIK2get"></a>
|
|
|
|
<b>Get</b>
|
|
|
|
<ul>
|
|
|
|
No get implemented yet ...
|
|
|
|
</ul>
|
|
|
|
<br>
|
|
|
|
|
|
|
|
<a name="LUXTRONIK2attr"></a>
|
|
|
|
<b>Attributes</b>
|
|
|
|
<ul>
|
|
|
|
<li>statusHTML<br>
|
|
|
|
if set, creates a HTML-formatted reading named "floorplanHTML" for use with the <a href="#FLOORPLAN">FLOORPLAN</a> module.<br>
|
|
|
|
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.</li>
|
2014-01-18 15:12:15 +00:00
|
|
|
<li>allowSetParameter<br>
|
|
|
|
<a href="#LUXTRONIK2set">Parameters</a> of the heatpump controller can only be changed if this attribut is set to 'yes'.</li>
|
2013-01-01 14:21:40 +00:00
|
|
|
<li><a href="#do_not_notify">do_not_notify</a></li>
|
|
|
|
</ul>
|
|
|
|
<br>
|
|
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
=end html
|
|
|
|
=cut
|
2014-01-16 18:17:49 +00:00
|
|
|
|