From d31bb5b5f0c3aa77489127d48e591bc22cbc17d3 Mon Sep 17 00:00:00 2001 From: tpoitzsch Date: Tue, 11 Feb 2014 18:17:43 +0000 Subject: [PATCH] html tags fixed git-svn-id: https://svn.fhem.de/fhem/trunk@4883 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/23_LUXTRONIK2.pm | 1340 ++++++++++++++++++------------------ fhem/FHEM/70_JSONMETER.pm | 726 +++++++++---------- 2 files changed, 1042 insertions(+), 1024 deletions(-) diff --git a/fhem/FHEM/23_LUXTRONIK2.pm b/fhem/FHEM/23_LUXTRONIK2.pm index 9c2a57425..c5306ef79 100644 --- a/fhem/FHEM/23_LUXTRONIK2.pm +++ b/fhem/FHEM/23_LUXTRONIK2.pm @@ -7,7 +7,8 @@ # (c) 2012,2014 Torsten Poitzsch (torsten.poitzsch@gmx.de) # (c) 2012-2013 Jan-Hinrich Fessel (oskar@fessel.org) # -# The modul reads and writes parameters of the heat pump controller Luxtronik 2.0 +# The modul reads and writes parameters of the heat pump controller +# Luxtronik 2.0 used in Alpha Innotec and Siemens Novelan (WPR NET) heat pumps. # # 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 @@ -57,12 +58,12 @@ LUXTRONIK2_Initialize($) $hash->{SetFn} = "LUXTRONIK2_Set"; $hash->{AttrFn} = "LUXTRONIK2_Attr"; $hash->{AttrList} = "disable:0,1 ". - "allowSetParameter:0,1 ". - "autoSynchClock:slider,10,5,300 ". - "doStatistics:0,1 ". - "ignoreFirmwareCheck:0,1 ". - "statusHTML ". - $readingFnAttributes; + "allowSetParameter:0,1 ". + "autoSynchClock:slider,10,5,300 ". + "doStatistics:0,1 ". + "ignoreFirmwareCheck:0,1 ". + "statusHTML ". + $readingFnAttributes; } sub ######################################## @@ -71,7 +72,8 @@ LUXTRONIK2_Define($$) my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); - return "Usage: define LUXTRONIK2 [poll-interval]" if(@a <3 || @a >4); + return "Usage: define LUXTRONIK2 [poll-interval]" + if(@a <3 || @a >4); my $name = $a[0]; my $host = $a[2]; @@ -123,65 +125,65 @@ LUXTRONIK2_Notify(@) { if ($dev->{NAME} eq "global" && grep (m/^INITIALIZED|REREADCFG$/,@{$dev->{CHANGED}})){ # housekeeping - my %cleanUp = ( - delayDeviceTime => "delayDeviceTimeCalc", - deviceTimeStartReadings => "deviceTimeCalc", - heatingSummerMode => "heatingLimit", - thresholdTemperatureSummerMode => "thresholdHeatingLimit", - lastDeviceClockSynch => "deviceTimeLastSync", - operatingHoursHeatPump => "counterHoursHeatPump", - operatingHoursSecondHeatSource1 => "counterHours2ndHeatSource1", - operatingHoursSecondHeatSource2 => "counterHours2ndHeatSource2", - operatingHoursSecondHeatSource3 => "counterHours2ndHeatSource3", - operatingHoursHeating => "counterHoursHeating", - operatingHoursHotWater => "counterHoursHotWater", - heatQuantityHeating => "counterHeatQHeating", - heatQuantityHotWater => "counterHeatQHotWater", - heatQuantityTotal => "counterHeatQTotal", - currentOperatingStatus1 => "opStateHeatPump1", - currentOperatingState1 => "opStateHeatPump1", - currentOperatingStatus2 => "opStateHeatPump3", - currentOperatingState2 => "opStateHeatPump2", - currentOperatingState3 => "opStateHeatPump3", - heatingOperatingMode => "opModeHeating", - heatingOperatingState => "opStateHeating", - hotWaterOperatingMode => "opModeHotWater", - hotWaterStatus => "opStateHotWater", - hotWaterState => "opStateHotWater", - heatingSystemCirculationPump => "heatingSystemCircPump", - hotWaterCirculationPumpExtern => "hotWaterCircPumpExtern", - statGradientBoilerTempLoss => "statBoilerGradientHeatUp' and 'statBoilerGradientCoolDown" ); - my $oldReading; - my $newReading; - while (($oldReading, $newReading) = each(%cleanUp)) { - if ( exists( $hash->{READINGS}{$oldReading} ) ) { - delete($hash->{READINGS}{$oldReading}); - Log3 $name,2,"$name: !!! Change/fix in LUXTRONIK2-Modul: '$oldReading' is now '$newReading'"; - } - } - } - return; + my %cleanUp = ( + delayDeviceTime => "delayDeviceTimeCalc", + deviceTimeStartReadings => "deviceTimeCalc", + heatingSummerMode => "heatingLimit", + thresholdTemperatureSummerMode => "thresholdHeatingLimit", + lastDeviceClockSynch => "deviceTimeLastSync", + operatingHoursHeatPump => "counterHoursHeatPump", + operatingHoursSecondHeatSource1 => "counterHours2ndHeatSource1", + operatingHoursSecondHeatSource2 => "counterHours2ndHeatSource2", + operatingHoursSecondHeatSource3 => "counterHours2ndHeatSource3", + operatingHoursHeating => "counterHoursHeating", + operatingHoursHotWater => "counterHoursHotWater", + heatQuantityHeating => "counterHeatQHeating", + heatQuantityHotWater => "counterHeatQHotWater", + heatQuantityTotal => "counterHeatQTotal", + currentOperatingStatus1 => "opStateHeatPump1", + currentOperatingState1 => "opStateHeatPump1", + currentOperatingStatus2 => "opStateHeatPump3", + currentOperatingState2 => "opStateHeatPump2", + currentOperatingState3 => "opStateHeatPump3", + heatingOperatingMode => "opModeHeating", + heatingOperatingState => "opStateHeating", + hotWaterOperatingMode => "opModeHotWater", + hotWaterStatus => "opStateHotWater", + hotWaterState => "opStateHotWater", + heatingSystemCirculationPump => "heatingSystemCircPump", + hotWaterCirculationPumpExtern => "hotWaterCircPumpExtern", + statGradientBoilerTempLoss => "statBoilerGradientHeatUp' and 'statBoilerGradientCoolDown" ); + my $oldReading; + my $newReading; + while (($oldReading, $newReading) = each(%cleanUp)) { + if ( exists( $hash->{READINGS}{$oldReading} ) ) { + delete($hash->{READINGS}{$oldReading}); + Log3 $name,2,"$name: !!! Change/fix in LUXTRONIK2-Modul: '$oldReading' is now '$newReading'"; + } + } + } + return; } sub ######################################## LUXTRONIK2_Attr(@) { - my ($cmd,$name,$aName,$aVal) = @_; - # $cmd can be "del" or "set" - # $name is device name - # aName and aVal are Attribute name and value - if ($cmd eq "set") { - if ($aName eq "1allowSetParameter") { - eval { qr/$aVal/ }; - if ($@) { - Log3 $name, 3, "LUXTRONIK2: Invalid allowSetParameter in attr $name $aName $aVal: $@"; - return "Invalid allowSetParameter $aVal"; - } - } - } - - return undef; + my ($cmd,$name,$aName,$aVal) = @_; + # $cmd can be "del" or "set" + # $name is device name + # aName and aVal are Attribute name and value + if ($cmd eq "set") { + if ($aName eq "1allowSetParameter") { + eval { qr/$aVal/ }; + if ($@) { + Log3 $name, 3, "LUXTRONIK2: Invalid allowSetParameter in attr $name $aName $aVal: $@"; + return "Invalid allowSetParameter $aVal"; + } + } + } + + return undef; } @@ -198,69 +200,69 @@ LUXTRONIK2_Set($$@) return undef; } elsif ($cmd eq 'resetStatistics') { - if ( ($val eq "all" || $val eq "statBoilerGradientCoolDownMin") - && exists($defs{$name}{READINGS}{statBoilerGradientCoolDownMin})) { - delete $defs{$name}{READINGS}{statBoilerGradientCoolDownMin}; - $resultStr .= " statBoilerGradientCoolDownMin"; - } - if ( $resultStr eq "" ) { - $resultStr = "$name: No statistics to reset"; - } else { - $resultStr = "$name: Statistic value(s) deleted:" . $resultStr; - } - Log3 $hash, 3, $resultStr; - return $resultStr; + if ( ($val eq "all" || $val eq "statBoilerGradientCoolDownMin") + && exists($defs{$name}{READINGS}{statBoilerGradientCoolDownMin})) { + delete $defs{$name}{READINGS}{statBoilerGradientCoolDownMin}; + $resultStr .= " statBoilerGradientCoolDownMin"; + } + if ( $resultStr eq "" ) { + $resultStr = "$name: No statistics to reset"; + } else { + $resultStr = "$name: Statistic value(s) deleted:" . $resultStr; + } + Log3 $hash, 3, $resultStr; + return $resultStr; } elsif($cmd eq 'INTERVAL' && int(@_)==4 ) { - $val = 30 if( $val < 30 ); - $hash->{INTERVAL}=$val; - return "Polling interval set to $val seconds."; + $val = 30 if( $val < 30 ); + $hash->{INTERVAL}=$val; + return "Polling interval set to $val seconds."; } #Check Firmware and Set-Paramter-lock if ($cmd eq 'synchronizeClockHeatPump' || - $cmd eq 'hotWaterTemperatureTarget' || - $cmd eq 'opModeHotWater') + $cmd eq 'hotWaterTemperatureTarget' || + $cmd eq 'opModeHotWater') { - my $firmware = ReadingsVal($name,"firmware",""); - my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware); + my $firmware = ReadingsVal($name,"firmware",""); + my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware); # stop in case of incompatible firmware - if ($firmwareCheck eq "fwNotCompatible") { - Log3 $name, 3, $name." Error: Host firmware '$firmware' not compatible for parameter setting."; - return "Firmware '$firmware' not compatible for parameter setting. "; + if ($firmwareCheck eq "fwNotCompatible") { + Log3 $name, 3, $name." Error: Host firmware '$firmware' not compatible for parameter setting."; + return "Firmware '$firmware' not compatible for parameter setting. "; # stop in case of untested firmware and firmware check enabled - } elsif (AttrVal($name, "ignoreFirmwareCheck", 0)!= 1 && - $firmwareCheck eq "fwNotTested") { - Log3 $name, 3, $name." Error: Host firmware '$firmware' not tested for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1"; - return "Firmware '$firmware' not compatible for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1."; + } elsif (AttrVal($name, "ignoreFirmwareCheck", 0)!= 1 && + $firmwareCheck eq "fwNotTested") { + Log3 $name, 3, $name." Error: Host firmware '$firmware' not tested for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1"; + return "Firmware '$firmware' not compatible for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1."; # stop in case setting of parameters is not enabled - } elsif ( AttrVal($name, "allowSetParameter", 0) != 1) { - Log3 $name, 3, $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1"; - return "Setting of parameters not allowed. To unlock, please set attribut 'allowSetParameter' to 1."; + } elsif ( AttrVal($name, "allowSetParameter", 0) != 1) { + Log3 $name, 3, $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1"; + return "Setting of parameters not allowed. To unlock, please set attribut 'allowSetParameter' to 1."; } } if ($cmd eq 'synchronizeClockHeatPump') { - $hash->{LOCAL} = 1; - $resultStr = LUXTRONIK2_synchronizeClock($hash); - $hash->{LOCAL} = 0; - Log3 $name, 3, "$name - $resultStr"; - return $resultStr; - } elsif(int(@_)==4 && - ($cmd eq 'hotWaterTemperatureTarget' - || $cmd eq 'opModeHotWater')) { - $hash->{LOCAL} = 1; - $resultStr = LUXTRONIK2_SetParameter ($hash, $cmd, $val); - $hash->{LOCAL} = 0; - return $resultStr; - } + $hash->{LOCAL} = 1; + $resultStr = LUXTRONIK2_synchronizeClock($hash); + $hash->{LOCAL} = 0; + Log3 $name, 3, "$name - $resultStr"; + return $resultStr; + } elsif(int(@_)==4 && + ($cmd eq 'hotWaterTemperatureTarget' + || $cmd eq 'opModeHotWater')) { + $hash->{LOCAL} = 1; + $resultStr = LUXTRONIK2_SetParameter ($hash, $cmd, $val); + $hash->{LOCAL} = 0; + return $resultStr; + } my $list = "statusRequest:noArg". - " resetStatistics:all,statBoilerGradientCoolDownMin". - " hotWaterTemperatureTarget:slider,30.0,0.5,65.0". - " opModeHotWater:Auto,Party,Off". - " synchronizeClockHeatPump:noArg". - " INTERVAL:slider,30,30,1800"; + " resetStatistics:all,statBoilerGradientCoolDownMin". + " hotWaterTemperatureTarget:slider,30.0,0.5,65.0". + " opModeHotWater:Auto,Party,Off". + " synchronizeClockHeatPump:noArg". + " INTERVAL:slider,30,30,1800"; return "Unknown argument $cmd, choose one of $list"; } @@ -301,10 +303,10 @@ LUXTRONIK2_DoUpdate($) Log3 $name, 5, "$name: Opening connection to host ".$host; my $socket = new IO::Socket::INET ( - PeerAddr => $host, - PeerPort => 8888, - # Type = SOCK_STREAM, # probably needed on some systems - Proto => 'tcp' + PeerAddr => $host, + PeerPort => 8888, + # Type = SOCK_STREAM, # probably needed on some systems + Proto => 'tcp' ); if (!$socket) { Log3 $name, 1, "$name Error: Could not open connection to host ".$host; @@ -325,7 +327,7 @@ LUXTRONIK2_DoUpdate($) $count = unpack("N", $result); if($count != 3004) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: Fetching operational values - wrong echo of request 3004: ".length($result)." -> ".$count; - $socket->close(); + $socket->close(); return "$name|0|3004 != $count"; } @@ -334,7 +336,7 @@ LUXTRONIK2_DoUpdate($) $count = unpack("N", $result); if($count > 0) { Log3 $name, 4, "$name: Parameter on target changed, restart parameter reading after 5 seconds"; - $socket->close(); + $socket->close(); return "$name|2|Status = $count - parameter on target changed, restart device reading after 5 seconds"; } @@ -343,7 +345,7 @@ LUXTRONIK2_DoUpdate($) my $count_calc_values = unpack("N", $result); if($count_calc_values == 0) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: Fetching operational values - 0 values announced: ".length($result)." -> ".$count_calc_values; - $socket->close(); + $socket->close(); return "$name|0|0 values read"; } @@ -352,13 +354,13 @@ LUXTRONIK2_DoUpdate($) $result=""; my $buf=""; while($i<=$count_calc_values) { - $socket->recv($buf,4); + $socket->recv($buf,4); $result.=$buf; - $i++; + $i++; } if(length($result) != $count_calc_values*4) { Log3 $name, 1, "$name LUXTRONIK2_DoUpdate-Error: operational values length check: ".length($result)." should have been ". $count_calc_values * 4; - $socket->close(); + $socket->close(); return "$name|0|Number of values read mismatch ( $!)\n"; } @@ -366,7 +368,7 @@ LUXTRONIK2_DoUpdate($) @heatpump_values = unpack("N$count_calc_values", $result); if(scalar(@heatpump_values) != $count_calc_values) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: unpacking problem by operation values: ".scalar(@heatpump_values)." instead of ".$count_calc_values; - $socket->close(); + $socket->close(); return "$name|0|Unpacking problem of operational values"; } @@ -395,7 +397,7 @@ LUXTRONIK2_DoUpdate($) my $count_set_parameter = unpack("N", $result); if($count_set_parameter == 0) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: 0 parameter read: ".length($result)." -> ".$count_set_parameter; - $socket->close(); + $socket->close(); return "$name|0|0 parameter read"; } @@ -404,20 +406,20 @@ LUXTRONIK2_DoUpdate($) $result=""; $buf=""; while($i<=$count_set_parameter) { - $socket->recv($buf,4); + $socket->recv($buf,4); $result.=$buf; - $i++; + $i++; } if(length($result) != $count_set_parameter*4) { Log3 $name, 1, "$name LUXTRONIK2_DoUpdate-Error: parameter length check: ".length($result)." should have been ". $count_set_parameter * 4; - $socket->close(); + $socket->close(); return "$name|0|Number of parameters read mismatch ( $!)\n"; } @heatpump_parameters = unpack("N$count_set_parameter", $result); if(scalar(@heatpump_parameters) != $count_set_parameter) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: unpacking problem by set parameter: ".scalar(@heatpump_parameters)." instead of ".$count_set_parameter; - $socket->close(); + $socket->close(); return "$name|0|Unpacking problem of set parameters"; } @@ -447,7 +449,7 @@ goto SKIP_VISIBILITY_READING; $count = unpack("N", $result); if($count == 0) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: 0 visibility attributes announced: ".length($result)." -> ".$count; - $socket->close(); + $socket->close(); return "$name|0|0 visibility attributes announced"; } @@ -456,20 +458,20 @@ goto SKIP_VISIBILITY_READING; $result=""; $buf=""; while($i<=$count) { - $socket->recv($buf,1); + $socket->recv($buf,1); $result.=$buf; - $i++; + $i++; } if(length($result) != $count) { Log3 $name, 1, "$name LUXTRONIK2_DoUpdate-Error: Visibility attributes length check: ".length($result)." should have been ". $count; - $socket->close(); + $socket->close(); return "$name|0|Number of Visibility attributes read mismatch ( $!)\n"; } @heatpump_visibility = unpack("C$count", $result); if(scalar(@heatpump_visibility) != $count) { Log3 $name, 2, "$name LUXTRONIK2_DoUpdate-Error: Unpacking problem by visibility attributes: ".scalar(@heatpump_visibility)." instead of ".$count; - $socket->close(); + $socket->close(); return "$name|0|Unpacking problem of visibility attributes"; } @@ -615,300 +617,300 @@ LUXTRONIK2_UpdateDone($) #Define Status Messages my %wpOpStat1 = ( 0 => "Waermepumpe laeuft", - 1 => "Waermepumpe steht", - 2 => "Waermepumpe kommt", - 4 => "Fehler", - 5 => "Abtauen", - 6 => "Warte auf LIN-Verbindung", - 7 => "Verdichter heizt auf"); + 1 => "Waermepumpe steht", + 2 => "Waermepumpe kommt", + 4 => "Fehler", + 5 => "Abtauen", + 6 => "Warte auf LIN-Verbindung", + 7 => "Verdichter heizt auf"); my %wpOpStat2 = ( 0 => "Heizbetrieb", - 1 => "Keine Anforderung", - 2 => "Netz Einschaltverzoegerung", - 3 => "Schaltspielzeit", - 4 => "EVU Sperrzeit", - 5 => "Brauchwasser", - 6 => "Stufe", - 7 => "Abtauen", - 8 => "Pumpenvorlauf", - 9 => "Thermische Desinfektion", - 10 => "Kuehlbetrieb", - 12 => "Schwimmbad/Photovoltaik", - 13 => "Heizen_Ext_En", - 14 => "Brauchw_Ext_En", - 16 => "Durchflussueberwachung", - 17 => "Elektrische Zusatzheizung" ); + 1 => "Keine Anforderung", + 2 => "Netz Einschaltverzoegerung", + 3 => "Schaltspielzeit", + 4 => "EVU Sperrzeit", + 5 => "Brauchwasser", + 6 => "Stufe", + 7 => "Abtauen", + 8 => "Pumpenvorlauf", + 9 => "Thermische Desinfektion", + 10 => "Kuehlbetrieb", + 12 => "Schwimmbad/Photovoltaik", + 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" ); + 1 => "Zusatzheizung", + 2 => "Party", + 3 => "Ferien", + 4 => "Aus" ); my %heatingState = ( 0 => "Abgesenkt", - 1 => "Normal", - 3 => "Aus"); + 1 => "Normal", + 3 => "Aus"); my %wpType = ( 0 => "ERC", 1 => "SW1", - 2 => "SW2", 3 => "WW1", - 4 => "WW2", 5 => "L1I", - 6 => "L2I", 7 => "L1A", - 8 => "L2A", 9 => "KSW", - 10 => "KLW", 11 => "SWC", - 12 => "LWC", 13 => "L2G", - 14 => "WZS", 15 => "L1I407", - 16 => "L2I407", 17 => "L1A407", - 18 => "L2A407", 19 => "L2G407", - 20 => "LWC407", 21 => "L1AREV", - 22 => "L2AREV", 23 => "WWC1", - 24 => "WWC2", 25 => "L2G404", - 26 => "WZW", 27 => "L1S", - 28 => "L1H", 29 => "L2H", - 30 => "WZWD", 31 => "ERC", - 40 => "WWB_20", 41 => "LD5", - 42 => "LD7", 43 => "SW 37_45", - 44 => "SW 58_69", 45 => "SW 29_56", - 46 => "LD5 (230V)", 47 => "LD7 (230 V)", - 48 => "LD9", 49 => "LD5 REV", - 50 => "LD7 REV", 51 => "LD5 REV 230V", - 52 => "LD7 REV 230V", 53 => "LD9 REV 230V", - 54 => "SW 291", 55 => "LW SEC" ); - + 2 => "SW2", 3 => "WW1", + 4 => "WW2", 5 => "L1I", + 6 => "L2I", 7 => "L1A", + 8 => "L2A", 9 => "KSW", + 10 => "KLW", 11 => "SWC", + 12 => "LWC", 13 => "L2G", + 14 => "WZS", 15 => "L1I407", + 16 => "L2I407", 17 => "L1A407", + 18 => "L2A407", 19 => "L2G407", + 20 => "LWC407", 21 => "L1AREV", + 22 => "L2AREV", 23 => "WWC1", + 24 => "WWC2", 25 => "L2G404", + 26 => "WZW", 27 => "L1S", + 28 => "L1H", 29 => "L2H", + 30 => "WZWD", 31 => "ERC", + 40 => "WWB_20", 41 => "LD5", + 42 => "LD7", 43 => "SW 37_45", + 44 => "SW 58_69", 45 => "SW 29_56", + 46 => "LD5 (230V)", 47 => "LD7 (230 V)", + 48 => "LD9", 49 => "LD5 REV", + 50 => "LD7 REV", 51 => "LD5 REV 230V", + 52 => "LD7 REV 230V", 53 => "LD9 REV 230V", + 54 => "SW 291", 55 => "LW SEC" ); + my $counterRetry = $hash->{fhem}{counterRetry}; - $counterRetry++; + $counterRetry++; if ($a[1]==0 ) { readingsSingleUpdate($hash,"state","Error: ".$a[2],1); - $counterRetry = 0; + $counterRetry = 0; } elsif ($a[1]==2 ) { if ($counterRetry <=3) { - InternalTimer(gettimeofday() + 5, "LUXTRONIK2_GetUpdate", $hash, 0); + 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"; - } + 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"; + } } elsif ($a[1]==1 ) { $counterRetry = 0; - readingsBeginUpdate($hash); + readingsBeginUpdate($hash); # Temporary storage of values because needed several times - my $ambientTemperature = LUXTRONIK2_CalcTemp($a[12]); - my $averageAmbientTemperature = LUXTRONIK2_CalcTemp($a[13]); - my $hotWaterTemperature = LUXTRONIK2_CalcTemp($a[14]); - my $hotWaterTemperatureTarget = LUXTRONIK2_CalcTemp($a[25]); - my $hotWaterTemperatureThreshold = LUXTRONIK2_CalcTemp($a[25] - $a[49]); + my $ambientTemperature = LUXTRONIK2_CalcTemp($a[12]); + my $averageAmbientTemperature = LUXTRONIK2_CalcTemp($a[13]); + my $hotWaterTemperature = LUXTRONIK2_CalcTemp($a[14]); + my $hotWaterTemperatureTarget = LUXTRONIK2_CalcTemp($a[25]); + my $hotWaterTemperatureThreshold = LUXTRONIK2_CalcTemp($a[25] - $a[49]); my $thresholdHeatingLimit = LUXTRONIK2_CalcTemp($a[21]); - my $thresholdTemperatureSetBack = LUXTRONIK2_CalcTemp($a[48]); - my $flowTemperature = LUXTRONIK2_CalcTemp($a[15]); - my $returnTemperature = LUXTRONIK2_CalcTemp($a[16]); - - # if selected, do all the statistic calculations - if ( AttrVal($name,"doStatistics",0) == 1) { - #LUXTRONIK2_doStatisticBoilerHeatUp $hash, $currOpHours, $currHQ, $currTemp, $opState, $target - $value = LUXTRONIK2_doStatisticBoilerHeatUp ($hash, $a[35], $a[37]/10, $hotWaterTemperature, $a[3],$hotWaterTemperatureTarget); - if ($value ne "") { - readingsBulkUpdate($hash,"statBoilerGradientHeatUp",$value); - Log3 $name,3,"$name: statBoilerGradientHeatUp set to $value"; - } - #LUXTRONIK2_doStatisticBoilerCoolDown $hash, $time, $currTemp, $opState, $target, $threshold - $value = LUXTRONIK2_doStatisticBoilerCoolDown ($hash, $a[22], $hotWaterTemperature, $a[3], $hotWaterTemperatureTarget, $hotWaterTemperatureThreshold); - if ($value ne "") { - readingsBulkUpdate($hash,"statBoilerGradientCoolDown",$value); - Log3 $name,3,"$name: statBoilerGradientCoolDown set to $value"; - if ( exists( $hash->{READINGS}{statBoilerGradientCoolDownMin} ) ) { - my @new = split / /, $value; - my @old = split / /, $hash->{READINGS}{statBoilerGradientCoolDownMin}; - if ($new[5]>6 && $new[0]<$old[0]) { - readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value); - Log3 $name,3,"$name: statBoilerGradientCoolDownMin set to $value"; - } - } else { - readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value); - Log3 $name,3,"$name: statBoilerGradientCoolDownMin set to $value"; - } - } - } + my $thresholdTemperatureSetBack = LUXTRONIK2_CalcTemp($a[48]); + my $flowTemperature = LUXTRONIK2_CalcTemp($a[15]); + my $returnTemperature = LUXTRONIK2_CalcTemp($a[16]); + + # if selected, do all the statistic calculations + if ( AttrVal($name,"doStatistics",0) == 1) { + #LUXTRONIK2_doStatisticBoilerHeatUp $hash, $currOpHours, $currHQ, $currTemp, $opState, $target + $value = LUXTRONIK2_doStatisticBoilerHeatUp ($hash, $a[35], $a[37]/10, $hotWaterTemperature, $a[3],$hotWaterTemperatureTarget); + if ($value ne "") { + readingsBulkUpdate($hash,"statBoilerGradientHeatUp",$value); + Log3 $name,3,"$name: statBoilerGradientHeatUp set to $value"; + } + #LUXTRONIK2_doStatisticBoilerCoolDown $hash, $time, $currTemp, $opState, $target, $threshold + $value = LUXTRONIK2_doStatisticBoilerCoolDown ($hash, $a[22], $hotWaterTemperature, $a[3], $hotWaterTemperatureTarget, $hotWaterTemperatureThreshold); + if ($value ne "") { + readingsBulkUpdate($hash,"statBoilerGradientCoolDown",$value); + Log3 $name,3,"$name: statBoilerGradientCoolDown set to $value"; + if ( exists( $hash->{READINGS}{statBoilerGradientCoolDownMin} ) ) { + my @new = split / /, $value; + my @old = split / /, $hash->{READINGS}{statBoilerGradientCoolDownMin}; + if ($new[5]>6 && $new[0]<$old[0]) { + readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value); + Log3 $name,3,"$name: statBoilerGradientCoolDownMin set to $value"; + } + } else { + readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value); + Log3 $name,3,"$name: statBoilerGradientCoolDownMin set to $value"; + } + } + } #Operating status of heat pump - my $opStateHeatPump1 = $wpOpStat1{$a[2]}; ############## + my $opStateHeatPump1 = $wpOpStat1{$a[2]}; ############## $opStateHeatPump1 = "unbekannt (".$a[2].")" unless $opStateHeatPump1; - readingsBulkUpdate($hash,"opStateHeatPump1",$opStateHeatPump1); - - my $opStateHeatPump2 = "unknown ($a[40])"; ############## - my $prefix = ""; - if ($a[40] == 0 || $a[40] == 2) { $prefix = "seit ";} - elsif ($a[40] == 1) { $prefix = "in ";} - if ($a[40] == 2) { #Sonderbehandlung bei WP-Fehlern - $opStateHeatPump2 = $prefix . strftime "%d.%m.%Y %H:%M:%S", localtime($a[42]); - } else { - $opStateHeatPump2 = $prefix . LUXTRONIK2_FormatDuration($a[41]); - } - readingsBulkUpdate($hash,"opStateHeatPump2",$opStateHeatPump2); - - my $opStateHeatPump3 = $wpOpStat2{$a[3]}; ############## - # refine text of third state - if ($a[3]==6) { - $opStateHeatPump3 = "Stufe ".$a[4]." ".LUXTRONIK2_CalcTemp($a[5])." C "; - } + readingsBulkUpdate($hash,"opStateHeatPump1",$opStateHeatPump1); + + my $opStateHeatPump2 = "unknown ($a[40])"; ############## + my $prefix = ""; + if ($a[40] == 0 || $a[40] == 2) { $prefix = "seit ";} + elsif ($a[40] == 1) { $prefix = "in ";} + if ($a[40] == 2) { #Sonderbehandlung bei WP-Fehlern + $opStateHeatPump2 = $prefix . strftime "%d.%m.%Y %H:%M:%S", localtime($a[42]); + } else { + $opStateHeatPump2 = $prefix . LUXTRONIK2_FormatDuration($a[41]); + } + readingsBulkUpdate($hash,"opStateHeatPump2",$opStateHeatPump2); + + my $opStateHeatPump3 = $wpOpStat2{$a[3]}; ############## + # refine text of third state + if ($a[3]==6) { + $opStateHeatPump3 = "Stufe ".$a[4]." ".LUXTRONIK2_CalcTemp($a[5])." C "; + } elsif ($a[3]==7) { if ($a[6]==1) {$opStateHeatPump3 = "Abtauen (Kreisumkehr)";} else {$opStateHeatPump3 = "Luftabtauen";} } $opStateHeatPump3 = "unbekannt (".$a[3].")" unless $opStateHeatPump3; - readingsBulkUpdate($hash,"opStateHeatPump3",$opStateHeatPump3); - - # Hot water operating mode - $value = $wpMode{$a[7]}; - $value = "unbekannt (".$a[7].")" unless $value; - readingsBulkUpdate($hash,"opModeHotWater",$value); - # opStateHotWater - if ($a[8]==0) {$value="Sperrzeit";} + readingsBulkUpdate($hash,"opStateHeatPump3",$opStateHeatPump3); + + # Hot water operating mode + $value = $wpMode{$a[7]}; + $value = "unbekannt (".$a[7].")" unless $value; + readingsBulkUpdate($hash,"opModeHotWater",$value); + # opStateHotWater + if ($a[8]==0) {$value="Sperrzeit";} elsif ($a[8]==1 && $a[9]==1) {$value="Aufheizen";} elsif ($a[8]==1 && $a[9]==0) {$value="Temp. OK";} - elsif ($a[8]==3) {$value="Aus";} + elsif ($a[8]==3) {$value="Aus";} else {$value = "unbekannt (".$a[8]."/".$a[9].")";} - readingsBulkUpdate($hash,"opStateHotWater",$value); + readingsBulkUpdate($hash,"opStateHotWater",$value); # Heating operating mode - $value = $wpMode{$a[10]}; - $value = "unbekannt (".$a[10].")" unless $value; - readingsBulkUpdate($hash,"opModeHeating",$value); - # Heating operating state - # Consider also heating limit - if ($a[10] == 0 - && $a[11] == 1 - && $averageAmbientTemperature >= $thresholdHeatingLimit) { - $value = "Heizungsgrenze (Aus)"; - } else { - $value = $heatingState{$a[46]}; - $value = "unbekannt (".$a[46].")" unless $value; - # Consider heating reduction limit - if ($a[46] == 0) { - if ($thresholdTemperatureSetBack <= $ambientTemperature) { - $value .= " ".LUXTRONIK2_CalcTemp($a[47])." C"; - } else { - $value = "Normal da < $thresholdTemperatureSetBack C"; - } - } - } - readingsBulkUpdate($hash,"opStateHeating",$value); - + $value = $wpMode{$a[10]}; + $value = "unbekannt (".$a[10].")" unless $value; + readingsBulkUpdate($hash,"opModeHeating",$value); + # Heating operating state + # Consider also heating limit + if ($a[10] == 0 + && $a[11] == 1 + && $averageAmbientTemperature >= $thresholdHeatingLimit) { + $value = "Heizungsgrenze (Aus)"; + } else { + $value = $heatingState{$a[46]}; + $value = "unbekannt (".$a[46].")" unless $value; + # Consider heating reduction limit + if ($a[46] == 0) { + if ($thresholdTemperatureSetBack <= $ambientTemperature) { + $value .= " ".LUXTRONIK2_CalcTemp($a[47])." C"; + } else { + $value = "Normal da < $thresholdTemperatureSetBack C"; + } + } + } + readingsBulkUpdate($hash,"opStateHeating",$value); + # Device and reading times, delays and durations - $value = strftime "%Y-%m-%d %H:%M:%S", localtime($a[22]); - readingsBulkUpdate($hash, "deviceTimeCalc", $value); - my $delayDeviceTimeCalc=floor($a[29]-$a[22]+0.5); - readingsBulkUpdate($hash, "delayDeviceTimeCalc", $delayDeviceTimeCalc); - 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; - } + $value = strftime "%Y-%m-%d %H:%M:%S", localtime($a[22]); + readingsBulkUpdate($hash, "deviceTimeCalc", $value); + my $delayDeviceTimeCalc=floor($a[29]-$a[22]+0.5); + readingsBulkUpdate($hash, "delayDeviceTimeCalc", $delayDeviceTimeCalc); + 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; + } # Temperatures and flow rate - readingsBulkUpdate( $hash, "ambientTemperature", $ambientTemperature); - readingsBulkUpdate( $hash, "averageAmbientTemperature", $averageAmbientTemperature); - readingsBulkUpdate( $hash, "heatingLimit",$a[11]?"on":"off"); - readingsBulkUpdate( $hash, "thresholdHeatingLimit", $thresholdHeatingLimit); - readingsBulkUpdate( $hash, "thresholdTemperatureSetBack", $thresholdTemperatureSetBack); - readingsBulkUpdate( $hash, "hotWaterTemperature", $hotWaterTemperature); - readingsBulkUpdate( $hash, "hotWaterTemperatureTarget",$hotWaterTemperatureTarget); - readingsBulkUpdate( $hash, "flowTemperature", $flowTemperature); - readingsBulkUpdate( $hash, "returnTemperature", $returnTemperature); - 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,"heatingSystemCircPump",$a[27]?"on":"off"); - readingsBulkUpdate($hash,"hotWaterCircPumpExtern",$a[28]?"on":"off"); - readingsBulkUpdate($hash,"hotWaterSwitchingValve",$a[9]?"on":"off"); + readingsBulkUpdate( $hash, "ambientTemperature", $ambientTemperature); + readingsBulkUpdate( $hash, "averageAmbientTemperature", $averageAmbientTemperature); + readingsBulkUpdate( $hash, "heatingLimit",$a[11]?"on":"off"); + readingsBulkUpdate( $hash, "thresholdHeatingLimit", $thresholdHeatingLimit); + readingsBulkUpdate( $hash, "thresholdTemperatureSetBack", $thresholdTemperatureSetBack); + readingsBulkUpdate( $hash, "hotWaterTemperature", $hotWaterTemperature); + readingsBulkUpdate( $hash, "hotWaterTemperatureTarget",$hotWaterTemperatureTarget); + readingsBulkUpdate( $hash, "flowTemperature", $flowTemperature); + readingsBulkUpdate( $hash, "returnTemperature", $returnTemperature); + 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,"heatingSystemCircPump",$a[27]?"on":"off"); + readingsBulkUpdate($hash,"hotWaterCircPumpExtern",$a[28]?"on":"off"); + readingsBulkUpdate($hash,"hotWaterSwitchingValve",$a[9]?"on":"off"); - # bivalentLevel - readingsBulkUpdate($hash,"bivalentLevel",$a[43]); - - # Firmware - my $firmware = $a[20]; - readingsBulkUpdate($hash,"firmware",$firmware); - my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware); - # if unknown firmware, ask at each startup to inform comunity - if ($hash->{fhem}{alertFirmware} != 1 && $firmwareCheck eq "fwNotTested") { - $hash->{fhem}{alertFirmware} = 1; - Log3 $hash, 2, "$name Alert: Host uses untested Firmware '$a[20]'. Please inform FHEM comunity about compatibility."; - } - - # Type of Heatpump - $value = $wpType{$a[31]}; - $value = "unbekannt (".$a[31].")" unless $value; - readingsBulkUpdate($hash,"typeHeatpump",$value); + # bivalentLevel + readingsBulkUpdate($hash,"bivalentLevel",$a[43]); + + # Firmware + my $firmware = $a[20]; + readingsBulkUpdate($hash,"firmware",$firmware); + my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware); + # if unknown firmware, ask at each startup to inform comunity + if ($hash->{fhem}{alertFirmware} != 1 && $firmwareCheck eq "fwNotTested") { + $hash->{fhem}{alertFirmware} = 1; + Log3 $hash, 2, "$name Alert: Host uses untested Firmware '$a[20]'. Please inform FHEM comunity about compatibility."; + } + + # Type of Heatpump + $value = $wpType{$a[31]}; + $value = "unbekannt (".$a[31].")" unless $value; + readingsBulkUpdate($hash,"typeHeatpump",$value); - # Operating hours (seconds->hours) and heat quantities, write/create readings only if >0 - if ($a[32]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource1",floor($a[32]/360+0.5)/10);} - if ($a[38]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource2",floor($a[38]/360+0.5)/10);} - if ($a[39]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource3",floor($a[39]/360+0.5)/10);} - if ($a[33]>0) {readingsBulkUpdate($hash,"counterHoursHeatPump",floor($a[33]/360+0.5)/10);} - if ($a[34]>0) {readingsBulkUpdate($hash,"counterHoursHeating",floor($a[34]/360+0.5)/10);} - if ($a[35]>0) {readingsBulkUpdate($hash,"counterHoursHotWater",floor($a[35]/360+0.5)/10);} - if ($a[36]>0) {readingsBulkUpdate($hash,"counterHeatQHeating",$a[36]/10);} - if ($a[37]>0) {readingsBulkUpdate($hash,"counterHeatQHotWater",$a[37]/10);} - if ($a[36]+$a[37]>0) {readingsBulkUpdate($hash,"counterHeatQTotal",($a[36]+$a[37])/10);} - #WM[kW] = delta_Temp [K] * Durchfluss [l/h] / ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 30 & 40°C) * 0,994 [kg/l] (H2O Dichte bei 35°C) ) - $value = ($flowTemperature-$returnTemperature) * $a[19] / 866.65; - readingsBulkUpdate( $hash, "currentThermalOutput", sprintf("%.1f", $value)); - - # HTML for floorplan - if(AttrVal($name, "statusHTML", "none") ne "none") { - $value = "
" . $a[0] . "
"; - $value .= "$opStateHeatPump1
"; - $value .= "$opStateHeatPump2
"; - $value .= "$opStateHeatPump3
"; - $value .= "Brauchwasser: $hotWaterTemperature °C"; - readingsBulkUpdate($hash,"floorplanHTML",$value); - } + # Operating hours (seconds->hours) and heat quantities, write/create readings only if >0 + if ($a[32]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource1",floor($a[32]/360+0.5)/10);} + if ($a[38]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource2",floor($a[38]/360+0.5)/10);} + if ($a[39]>0) {readingsBulkUpdate($hash,"counterHours2ndHeatSource3",floor($a[39]/360+0.5)/10);} + if ($a[33]>0) {readingsBulkUpdate($hash,"counterHoursHeatPump",floor($a[33]/360+0.5)/10);} + if ($a[34]>0) {readingsBulkUpdate($hash,"counterHoursHeating",floor($a[34]/360+0.5)/10);} + if ($a[35]>0) {readingsBulkUpdate($hash,"counterHoursHotWater",floor($a[35]/360+0.5)/10);} + if ($a[36]>0) {readingsBulkUpdate($hash,"counterHeatQHeating",$a[36]/10);} + if ($a[37]>0) {readingsBulkUpdate($hash,"counterHeatQHotWater",$a[37]/10);} + if ($a[36]+$a[37]>0) {readingsBulkUpdate($hash,"counterHeatQTotal",($a[36]+$a[37])/10);} + #WM[kW] = delta_Temp [K] * Durchfluss [l/h] / ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 30 & 40°C) * 0,994 [kg/l] (H2O Dichte bei 35°C) ) + $value = ($flowTemperature-$returnTemperature) * $a[19] / 866.65; + readingsBulkUpdate( $hash, "currentThermalOutput", sprintf("%.1f", $value)); + + # HTML for floorplan + if(AttrVal($name, "statusHTML", "none") ne "none") { + $value = "
" . $a[0] . "
"; + $value .= "$opStateHeatPump1
"; + $value .= "$opStateHeatPump2
"; + $value .= "$opStateHeatPump3
"; + $value .= "Brauchwasser: $hotWaterTemperature °C"; + readingsBulkUpdate($hash,"floorplanHTML",$value); + } # State update - readingsBulkUpdate($hash,"state","$opStateHeatPump1 $opStateHeatPump2 - $opStateHeatPump3"); - + readingsBulkUpdate($hash,"state","$opStateHeatPump1 $opStateHeatPump2 - $opStateHeatPump3"); + readingsEndUpdate($hash,1); - - $hash->{helper}{fetched_calc_values} = $a[44]; - $hash->{helper}{fetched_parameters} = $a[45]; - - ############################ - #Auto Synchronize Device Clock - my $autoSynchClock = AttrVal($name, "autoSynchClock", 0); - $autoSynchClock = 10 unless ($autoSynchClock >= 10 || $autoSynchClock == 0); - $autoSynchClock = 600 unless $autoSynchClock <= 600; - if ($autoSynchClock != 0 and abs($delayDeviceTimeCalc) > $autoSynchClock ) { - Log3 $name, 3, $name." - autoSynchClock triggered (delayDeviceTimeCalc ".abs($delayDeviceTimeCalc)." > $autoSynchClock)."; - # Firmware not tested and Firmware Check not ignored - if ($firmwareCheck eq "fwNotTested" && AttrVal($name, "ignoreFirmwareCheck", 0)!= 1) { - Log3 $name, 1, $name." Error: Host firmware '$firmware' not tested for clock synchronization. To test set 'ignoreFirmwareCheck' to 1."; - $attr{$name}{autoSynchClock} = 0; - Log3 $name, 3, $name." Attribute 'autoSynchClock' set to 0."; - #Firmware not compatible - } elsif ($firmwareCheck eq "fwNotCompatible") { - Log3 $name, 1, $name." Error: Host firmware '$firmware' not compatible for host clock synchronization."; - $attr{$name}{autoSynchClock} = 0; - Log3 $name, 3, $name." Attribute 'autoSynchClock' set to 0."; - #Firmware OK -> Synchronize Clock - } else { - $value = LUXTRONIK2_synchronizeClock($hash, 600); - Log3 $hash, 3, "$name ".$value; - } - } - #End of Auto Synchronize Device Clock - ############################ - } - else { - Log3 $hash, 5, "$name LUXTRONIK2_DoUpdate-Error: Status = $a[1]"; - } + + $hash->{helper}{fetched_calc_values} = $a[44]; + $hash->{helper}{fetched_parameters} = $a[45]; + + ############################ + #Auto Synchronize Device Clock + my $autoSynchClock = AttrVal($name, "autoSynchClock", 0); + $autoSynchClock = 10 unless ($autoSynchClock >= 10 || $autoSynchClock == 0); + $autoSynchClock = 600 unless $autoSynchClock <= 600; + if ($autoSynchClock != 0 and abs($delayDeviceTimeCalc) > $autoSynchClock ) { + Log3 $name, 3, $name." - autoSynchClock triggered (delayDeviceTimeCalc ".abs($delayDeviceTimeCalc)." > $autoSynchClock)."; + # Firmware not tested and Firmware Check not ignored + if ($firmwareCheck eq "fwNotTested" && AttrVal($name, "ignoreFirmwareCheck", 0)!= 1) { + Log3 $name, 1, $name." Error: Host firmware '$firmware' not tested for clock synchronization. To test set 'ignoreFirmwareCheck' to 1."; + $attr{$name}{autoSynchClock} = 0; + Log3 $name, 3, $name." Attribute 'autoSynchClock' set to 0."; + #Firmware not compatible + } elsif ($firmwareCheck eq "fwNotCompatible") { + Log3 $name, 1, $name." Error: Host firmware '$firmware' not compatible for host clock synchronization."; + $attr{$name}{autoSynchClock} = 0; + Log3 $name, 3, $name." Attribute 'autoSynchClock' set to 0."; + #Firmware OK -> Synchronize Clock + } else { + $value = LUXTRONIK2_synchronizeClock($hash, 600); + Log3 $hash, 3, "$name ".$value; + } + } + #End of Auto Synchronize Device Clock + ############################ + } + else { + Log3 $hash, 5, "$name LUXTRONIK2_DoUpdate-Error: Status = $a[1]"; + } $hash->{fhem}{counterRetry} = $counterRetry; } @@ -960,28 +962,28 @@ LUXTRONIK2_SetParameter($$$) my $name = $hash->{NAME}; my %opMode = ( "Auto" => 0, - "Party" => 2, - "Off" => 4); + "Party" => 2, + "Off" => 4); if(AttrVal($name, "allowSetParameter", 0) != 1) { - return $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1"; + return $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1"; } 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; + $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 "opModeHotWater") { - if (! exists($opMode{$realValue})) { - return "$name Error: Wrong parameter given for opModeHotWater, use Automatik,Party,Off" - } - $setParameter = 4; - $setValue = $opMode{$realValue}; + if (! exists($opMode{$realValue})) { + return "$name Error: Wrong parameter given for opModeHotWater, use Automatik,Party,Off" + } + $setParameter = 4; + $setValue = $opMode{$realValue}; } else { return "$name LUXTRONIK2_SetParameter-Error: unknown parameter $parameterName"; @@ -991,47 +993,47 @@ LUXTRONIK2_SetParameter($$$) # 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 bytes 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 bytes 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"; + 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 bytes 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 bytes 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"; } } @@ -1048,41 +1050,41 @@ LUXTRONIK2_synchronizeClock (@) $maxDelta = 60 unless $maxDelta >= 0; $maxDelta = 600 unless $maxDelta <= 600; - + Log3 $name, 5, "$name: Open telnet connection to $host"; my $telnet = new Net::Telnet ( Host=>$host, Port => 23, Timeout=>10, Errmode=>'return'); if (!$telnet) { - Log3 $name, 1, "$name LUXTRONIK2_synchronizeClock-Error: ".$telnet->errmsg; - return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg; - } + Log3 $name, 1, "$name LUXTRONIK2_synchronizeClock-Error: ".$telnet->errmsg; + return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg; + } Log3 $name, 5, "$name: Log into $host"; - if (!$telnet->login('root', '')) { - Log3 $name, 1, "$name LUXTRONIK2_synchronizeClock-Error: ".$telnet->errmsg; - return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg; - } - + if (!$telnet->login('root', '')) { + Log3 $name, 1, "$name LUXTRONIK2_synchronizeClock-Error: ".$telnet->errmsg; + return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg; + } + Log3 $name, 5, "$name: Read current time of host"; my @output = $telnet->cmd('date +%s'); - $delay = floor(time()) - $output[0]; + $delay = floor(time()) - $output[0]; Log3 $name, 5, "$name: Current time is ".localtime($output[0])." Delay is $delay seconds."; if (abs($delay)>$maxDelta && $maxDelta!=0) { - $returnStr = "Do not dare to synchronize. Device clock of host $host differs by $delay seconds (max. is $maxDelta)."; + $returnStr = "Do not dare to synchronize. Device clock of host $host differs by $delay seconds (max. is $maxDelta)."; } elsif ($delay == 0) { - $returnStr = "Internal clock of host $host has no delay. -> not synchronized"; - } else { - my $newTime = strftime "%m%d%H%M%Y.%S", localtime(); - Log3 $name, 5, "$name: Run command 'date ".$newTime."'"; - @output=$telnet->cmd('date '.$newTime); - $returnStr = "Internal clock of host $host corrected by $delay seconds. -> ".$output[0]; - readingsSingleUpdate($hash,"deviceTimeLastSync",TimeNow,1); - } + $returnStr = "Internal clock of host $host has no delay. -> not synchronized"; + } else { + my $newTime = strftime "%m%d%H%M%Y.%S", localtime(); + Log3 $name, 5, "$name: Run command 'date ".$newTime."'"; + @output=$telnet->cmd('date '.$newTime); + $returnStr = "Internal clock of host $host corrected by $delay seconds. -> ".$output[0]; + readingsSingleUpdate($hash,"deviceTimeLastSync",TimeNow,1); + } Log3 $name, 5, "$name: Close telnet connection."; - $telnet->close; - - return $returnStr; + $telnet->close; + + return $returnStr; } sub ######################################## @@ -1091,15 +1093,15 @@ LUXTRONIK2_checkFirmware ($) my ($myFirmware) = @_; #Firmware not tested - if (index($testedFirmware,"#".$myFirmware."#") == -1) { - return "fwNotTested"; + if (index($testedFirmware,"#".$myFirmware."#") == -1) { + return "fwNotTested"; #Firmware tested but not compatible - } elsif (index($compatibleFirmware,"#".$myFirmware."#") == -1) { - return "fwNotCompatible"; + } elsif (index($compatibleFirmware,"#".$myFirmware."#") == -1) { + return "fwNotCompatible"; #Firmware compatible - } else { - return "fwCompatible"; - } + } else { + return "fwCompatible"; + } } # Calculate heat-up gradients of boiler based on hotWaterTemperature and counterHeatQHeating @@ -1108,163 +1110,163 @@ LUXTRONIK2_doStatisticBoilerHeatUp ($$$$$$) { my ($hash, $currOpHours, $currHQ, $currTemp, $opState, $target) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; my $step = $hash->{fhem}{statBoilerHeatUpStep}; - my $minTemp = $hash->{fhem}{statBoilerHeatUpMin}; - my $maxTemp = $hash->{fhem}{statBoilerHeatUpMax}; - my $lastHQ = $hash->{fhem}{statBoilerHeatUpHQ}; - my $lastOpHours = $hash->{fhem}{statBoilerHeatUpOpHours}; - my $value1 = 0; + my $minTemp = $hash->{fhem}{statBoilerHeatUpMin}; + my $maxTemp = $hash->{fhem}{statBoilerHeatUpMax}; + my $lastHQ = $hash->{fhem}{statBoilerHeatUpHQ}; + my $lastOpHours = $hash->{fhem}{statBoilerHeatUpOpHours}; + my $value1 = 0; my $value2 = 0; my $value3 = 0; - my $returnStr = ""; + my $returnStr = ""; # step 0 = Initialize - if hot water preparation is off if ($step == 0) { - if ($opState != 5) { # wait till hot water preparation stopped - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 0->1: Initializing Measurment"; - $step = 1; - $lastOpHours = $currOpHours; - $lastHQ = $currHQ; - $minTemp = $currTemp; - } - + if ($opState != 5) { # wait till hot water preparation stopped + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 0->1: Initializing Measurment"; + $step = 1; + $lastOpHours = $currOpHours; + $lastHQ = $currHQ; + $minTemp = $currTemp; + } + # step 1 = wait till hot water preparation starts -> monitor Tmin, take previous HQ and previous operating hours } elsif ($step == 1) { - if ($currTemp < $minTemp) { # monitor minimum temperature - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 1: Monitor minimum temperature ($minTemp -> $currTemp)"; - $minTemp = $currTemp; - } - if ($opState != 5) { # wait -> update operating hours and HQ to be used as start value in calculations - $lastOpHours = $currOpHours; - $lastHQ = $currHQ; - } else { # go to step 2 - if hot water preparation running - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 1->2: Hot water preparation started ".($currOpHours-$lastOpHours)." s ago"; - $step = 2; - $maxTemp = $currTemp; - } + if ($currTemp < $minTemp) { # monitor minimum temperature + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 1: Monitor minimum temperature ($minTemp -> $currTemp)"; + $minTemp = $currTemp; + } + if ($opState != 5) { # wait -> update operating hours and HQ to be used as start value in calculations + $lastOpHours = $currOpHours; + $lastHQ = $currHQ; + } else { # go to step 2 - if hot water preparation running + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 1->2: Hot water preparation started ".($currOpHours-$lastOpHours)." s ago"; + $step = 2; + $maxTemp = $currTemp; + } # step 2 = wait till hot water preparation done and target reached } elsif ($step == 2) { - if ($currTemp < $minTemp) { # monitor minimal temperature - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2: Boiler temperature still decreasing ($minTemp -> $currTemp)"; - $minTemp = $currTemp; - } - if ($currTemp > $maxTemp) { # monitor maximal temperature - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2: Boiler temperature increasing ($maxTemp -> $currTemp)"; - $maxTemp = $currTemp; - } - - if ($opState != 5) { # wait till hot water preparation stopped - if ($currTemp >= $target) { - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2->3: Hot water preparation stopped"; - $step = 3; - } else { - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2->1: Measurement cancelled (hot water preparation stopped but target not reached, $currTemp < $target)"; - $step = 1; - $lastOpHours = $currOpHours; - $lastHQ = $currHQ; - $minTemp = $currTemp; - } - } - + if ($currTemp < $minTemp) { # monitor minimal temperature + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2: Boiler temperature still decreasing ($minTemp -> $currTemp)"; + $minTemp = $currTemp; + } + if ($currTemp > $maxTemp) { # monitor maximal temperature + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2: Boiler temperature increasing ($maxTemp -> $currTemp)"; + $maxTemp = $currTemp; + } + + if ($opState != 5) { # wait till hot water preparation stopped + if ($currTemp >= $target) { + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2->3: Hot water preparation stopped"; + $step = 3; + } else { + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 2->1: Measurement cancelled (hot water preparation stopped but target not reached, $currTemp < $target)"; + $step = 1; + $lastOpHours = $currOpHours; + $lastHQ = $currHQ; + $minTemp = $currTemp; + } + } + # step 3 = wait with calculation till temperature maximum reached once } elsif ($step == 3) { # cancel measurement - if hot water preparation has restarted - if ($opState == 5) { - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3->0: Measurement cancelled (hot water preparation restarted before maximum reached)"; - $step = 0; - # monitor maximal temperature + if ($opState == 5) { + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3->0: Measurement cancelled (hot water preparation restarted before maximum reached)"; + $step = 0; + # monitor maximal temperature } elsif ($currTemp > $maxTemp) { - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3: Temperature still increasing ($maxTemp -> $currTemp)"; - $maxTemp = $currTemp; - # else calculate temperature gradient - } else { - Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3->1: Boiler heat-up measurement finished"; - $value1 = ( int(10 * $maxTemp) - int(10 * $minTemp) ) / 10; # delta hot water temperature - $value2 = ( $currOpHours - $lastOpHours ) / 60; # delta time (minutes) - # $value3 = floor(100 * $value1 / $value2 + 0.5) / 100; # Temperature gradient over time rounded to 1/100th - # $value2 = floor(100 * $value2 + 0.5) / 100; # rounded to 1/100th - $returnStr = "DT/min: ".sprintf("%.2f", $value1/$value2)." DT: ".sprintf("%.2f", $value1)." Dmin: ".sprintf("%.0f", $value2); + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3: Temperature still increasing ($maxTemp -> $currTemp)"; + $maxTemp = $currTemp; + # else calculate temperature gradient + } else { + Log3 $name, 4, "$name: Statistic Boiler Heat-Up step 3->1: Boiler heat-up measurement finished"; + $value1 = ( int(10 * $maxTemp) - int(10 * $minTemp) ) / 10; # delta hot water temperature + $value2 = ( $currOpHours - $lastOpHours ) / 60; # delta time (minutes) + # $value3 = floor(100 * $value1 / $value2 + 0.5) / 100; # Temperature gradient over time rounded to 1/100th + # $value2 = floor(100 * $value2 + 0.5) / 100; # rounded to 1/100th + $returnStr = "DT/min: ".sprintf("%.2f", $value1/$value2)." DT: ".sprintf("%.2f", $value1)." Dmin: ".sprintf("%.0f", $value2); - $value2 = $currHQ - $lastHQ; # delta heat quantity - # $value2 = floor(10*($currHQ - $lastHQ)+0.5)/10; # delta heat quantity - # $value3 = floor(100 * $value2 / $value1 + 0.5) / 100; # heat gradient over temperature rounded to 1/100th - $returnStr .= " DQ/T: ".sprintf("%.2f",$value2/$value1)." DQ: ".sprintf("%.1f",$value2); + $value2 = $currHQ - $lastHQ; # delta heat quantity + # $value2 = floor(10*($currHQ - $lastHQ)+0.5)/10; # delta heat quantity + # $value3 = floor(100 * $value2 / $value1 + 0.5) / 100; # heat gradient over temperature rounded to 1/100th + $returnStr .= " DQ/T: ".sprintf("%.2f",$value2/$value1)." DQ: ".sprintf("%.1f",$value2); - #Volumen [l] = Wärmemenge [kWh] / (delta T) [K] * ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 40°C) * 0,992 [kg/l] (H2O Dichte bei 40°C) ) [K/(kWh*l)] ) - $value3 = 868.4 * $value2 / $value1 ; # heated water volume in liter - $returnStr .= " DV: ".sprintf("%.0f",$value3); - - $step = 1; - $lastOpHours = $currOpHours; - $lastHQ = $currHQ; - $minTemp = $currTemp; + #Volumen [l] = Wärmemenge [kWh] / (delta T) [K] * ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 40°C) * 0,992 [kg/l] (H2O Dichte bei 40°C) ) [K/(kWh*l)] ) + $value3 = 868.4 * $value2 / $value1 ; # heated water volume in liter + $returnStr .= " DV: ".sprintf("%.0f",$value3); + + $step = 1; + $lastOpHours = $currOpHours; + $lastHQ = $currHQ; + $minTemp = $currTemp; - } - } + } + } $hash->{fhem}{statBoilerHeatUpStep} = $step; - $hash->{fhem}{statBoilerHeatUpMin} = $minTemp; - $hash->{fhem}{statBoilerHeatUpMax} = $maxTemp; - $hash->{fhem}{statBoilerHeatUpHQ} = $lastHQ; - $hash->{fhem}{statBoilerHeatUpOpHours} = $lastOpHours; - + $hash->{fhem}{statBoilerHeatUpMin} = $minTemp; + $hash->{fhem}{statBoilerHeatUpMax} = $maxTemp; + $hash->{fhem}{statBoilerHeatUpHQ} = $lastHQ; + $hash->{fhem}{statBoilerHeatUpOpHours} = $lastOpHours; + return $returnStr; - + } - + # Calculate heat loss gradients of boiler based on hotWaterTemperature and counterHeatQHeating sub ######################################## LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$) { my ($hash, $time, $currTemp, $opState, $target, $threshold) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; my $step = $hash->{fhem}{statBoilerCoolDownStep}; - my $maxTemp = $hash->{fhem}{statBoilerCoolDownMax}; - my $startTime = $hash->{fhem}{statBoilerCoolDownStartTime}; + my $maxTemp = $hash->{fhem}{statBoilerCoolDownMax}; + my $startTime = $hash->{fhem}{statBoilerCoolDownStartTime}; my $value1 = 0; my $value2 = 0; my $value3 = 0; - my $returnStr = ""; + my $returnStr = ""; # step 0 = Initialize - if hot water preparation is off and target reached, if ($step == 0) { - if ($opState == 5 || $currTemp < $target) { # -> stay step 0 - # Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 0: Wait till hot water preparation stops and target is reached ($currTemp < $target)"; - } else { - Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 0->1: Initializing, target reached ($currTemp >= $target)"; - $step = 1; - $startTime = $time; - $maxTemp = $currTemp; - } + if ($opState == 5 || $currTemp < $target) { # -> stay step 0 + # Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 0: Wait till hot water preparation stops and target is reached ($currTemp < $target)"; + } else { + Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 0->1: Initializing, target reached ($currTemp >= $target)"; + $step = 1; + $startTime = $time; + $maxTemp = $currTemp; + } # step 1 = wait till threshold is reached -> do calculation, monitor maximal temperature } elsif ($step == 1) { - if ($currTemp > $maxTemp) { # monitor maximal temperature - Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 1: Temperature still increasing ($currTemp > $maxTemp)"; - $maxTemp = $currTemp; - $startTime = $time; - } - if ($opState == 5) { - Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 1: Measurement cancelled (restart of hot water preparation)"; - $step = 0; - } elsif ($currTemp <= $threshold) { - Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 2->1: Measurement finished, threshold reached ($currTemp <= $threshold)"; - $value1 = ( int(10 * $currTemp) - int(10 * $maxTemp) ) / 10; # delta hot water temperature - $value2 = ( $time - $startTime ) / 3600; # delta time (hours) - $value3 = floor(100 * $value1 / $value2 + 0.5) / 100; # Temperature gradient over time rounded to 1/100th - $value2 = floor(100 * $value2 + 0.5) / 100; # rounded to 1/100th - $returnStr = "DT/h: $value3 DT: $value1 Dh: $value2"; + if ($currTemp > $maxTemp) { # monitor maximal temperature + Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 1: Temperature still increasing ($currTemp > $maxTemp)"; + $maxTemp = $currTemp; + $startTime = $time; + } + if ($opState == 5) { + Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 1: Measurement cancelled (restart of hot water preparation)"; + $step = 0; + } elsif ($currTemp <= $threshold) { + Log3 $name, 4, "$name: Statistic Boiler Cool-Down step 2->1: Measurement finished, threshold reached ($currTemp <= $threshold)"; + $value1 = ( int(10 * $currTemp) - int(10 * $maxTemp) ) / 10; # delta hot water temperature + $value2 = ( $time - $startTime ) / 3600; # delta time (hours) + $value3 = floor(100 * $value1 / $value2 + 0.5) / 100; # Temperature gradient over time rounded to 1/100th + $value2 = floor(100 * $value2 + 0.5) / 100; # rounded to 1/100th + $returnStr = "DT/h: $value3 DT: $value1 Dh: $value2"; - $step = 0; - } + $step = 0; + } } $hash->{fhem}{statBoilerCoolDownStep} = $step; - $hash->{fhem}{statBoilerCoolDownMax} = $maxTemp; - $hash->{fhem}{statBoilerCoolDownStartTime} = $startTime; + $hash->{fhem}{statBoilerCoolDownMax} = $maxTemp; + $hash->{fhem}{statBoilerCoolDownStartTime} = $startTime; - return $returnStr; -} + return $returnStr; +} 1; @@ -1295,55 +1297,54 @@ LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$)
- Set
+ Set
    A firmware check assures before each set operation that a heat pump with untested firmware is not damaged accidently. -
  • opModeHotWater <Mode>
    - Operating Mode of domestic hot water boiler (Auto | Party | Off)
  • -
  • hotWaterTemperatureTarget <temperature>
    - Target temperature of domestic hot water boiler in °C
  • -
  • INTERVAL <polling interval>
    - Polling interval in seconds
  • -
  • statusRequest
    - Update device information
  • -
  • synchClockHeatPump
    - Synchronizes controller clock with FHEM time. !! This change is lost in case of controller power off!!
  • -
-
- - Get -
    - No get implemented yet ... -
-
- - - Attributes -
    -
  • statusHTML -
    - If set, a HTML-formatted reading named "floorplanHTML" is created. It can be used with the FLOORPLAN module. -
    - Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heat pump and the temperature of the water.
  • -
  • doStatistics < 0 | 1 > -
    - Calculates statistic values: statBoilerGradientHeatUp, statBoilerGradientCoolDown, statBoilerGradientCoolDownMin (boiler heat loss)
  • -
  • allowSetParameter < 0 | 1 > -
    - The parameters of the heat pump controller can only be changed if this attribut is set to 1.
  • -
  • autoSynchClock <delay> -
    - Corrects the clock of the heatpump automatically if a certain delay (10 s - 600 s) against the FHEM time is exeeded. Does a firmware check before. -
    - (A 'delayDeviceTimeCalc' <= 2 s can be caused by the internal calculation interval of the heat pump controller.)
  • -
  • ignoreFirmwareCheck < 0 | 1 > -
    - A firmware check assures before each set operation that a heatpump controller with untested firmware is not damaged accidently. -
    - If this attribute is set to 1, the firmware check is ignored and new firmware can be tested for compatibility.
  • -
  • readingFnAttributes
  • -
-
+
  • opModeHotWater <Mode>
    + Operating Mode of domestic hot water boiler (Auto | Party | Off)
  • +
  • hotWaterTemperatureTarget <temperature>
    + Target temperature of domestic hot water boiler in °C
  • +
  • INTERVAL <polling interval>
    + Polling interval in seconds
  • +
  • statusRequest
    + Update device information
  • +
  • synchClockHeatPump
    + Synchronizes controller clock with FHEM time. !! This change is lost in case of controller power off!!
  • + +
    + + + Get +
      + No get implemented yet ... +
    +
    + + Attributes +
      +
    • statusHTML +
      + If set, a HTML-formatted reading named "floorplanHTML" is created. It can be used with the FLOORPLAN module. +
      + Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heat pump and the temperature of the water.
    • +
    • doStatistics < 0 | 1 > +
      + Calculates statistic values: statBoilerGradientHeatUp, statBoilerGradientCoolDown, statBoilerGradientCoolDownMin (boiler heat loss)
    • +
    • allowSetParameter < 0 | 1 > +
      + The parameters of the heat pump controller can only be changed if this attribut is set to 1.
    • +
    • autoSynchClock <delay> +
      + Corrects the clock of the heatpump automatically if a certain delay (10 s - 600 s) against the FHEM time is exeeded. Does a firmware check before. +
      + (A 'delayDeviceTimeCalc' <= 2 s can be caused by the internal calculation interval of the heat pump controller.)
    • +
    • ignoreFirmwareCheck < 0 | 1 > +
      + A firmware check assures before each set operation that a heatpump controller with untested firmware is not damaged accidently. +
      + If this attribute is set to 1, the firmware check is ignored and new firmware can be tested for compatibility.
    • +
    • readingFnAttributes
    • +
    =end html @@ -1362,7 +1363,7 @@ LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$) Define
      - define <name> LUXTRONIK2 <IP-Adresse> [Abfrage-Interval] + define <name> LUXTRONIK2 <IP-Adresse> [Abfrageinterval]
      Wenn das Abfrage-Interval nicht angegeben ist, wird es auf 300 (Sekunden) gesetzt. Der kleinste mögliche Wert ist 30.
      @@ -1374,29 +1375,29 @@ LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$) Set
        - Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass Wärmepumpen mit ungetester Firmware nicht unabsichtlich beschädigt werden. + Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass Wärmepumpen mit ungetester Firmware nicht unabsichtlich beschädigt werden.
      • opModeHotWater <Betriebsmodus> -
        - Betriebsmodus des Heißwasserboilers ( Auto | Party | Off ) -
      • hotWaterTemperatureTarget <Temperatur> -
        - Soll-Temperatur des Heißwasserboilers in °C
      • -
      • INTERVAL <Abfrageinterval> -
        - Abfrageinterval in Sekunden
      • -
      • statusRequest -
        - Aktualisieren der Gerätewerte
      • -
      • synchClockHeatPump -
        - Abgleich der Uhr der Steuerung mit der FHEM Zeit. Diese Änderung geht verloren, sobald die Steuerung ausgeschaltet wird!!
      • +
        + Betriebsmodus des Heißwasserboilers ( Auto | Party | Off ) +
      • hotWaterTemperatureTarget <Temperatur> +
        + Soll-Temperatur des Heißwasserboilers in °C
      • +
      • INTERVAL <Abfrageinterval> +
        + Abfrageinterval in Sekunden
      • +
      • statusRequest +
        + Aktualisieren der Gerätewerte
      • +
      • synchClockHeatPump +
        + Abgleich der Uhr der Steuerung mit der FHEM Zeit. Diese Änderung geht verloren, sobald die Steuerung ausgeschaltet wird!!

      Get
        - Es wurde noch kein "get" implementiert ... + Es wurde noch kein "get" implementiert ...

      @@ -1404,25 +1405,32 @@ LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$) Attribute
      • statusHTML
        - wenn gesetzt, dann wird ein HTML-formatierter Wert "floorplanHTML" erzeugt, welcher vom Modul FLOORPLAN genutzt werden kann.
        - Momentan wird nur geprüft, ob der Wert dieses Attributes ungleich NULL ist, der entsprechende Gerätewerte besteht aus dem aktuellen Wärmepumpenstatus und der Heizwassertemperatur.
      • + wenn gesetzt, dann wird ein HTML-formatierter Wert "floorplanHTML" erzeugt, + welcher vom Modul FLOORPLAN genutzt werden kann.
        + Momentan wird nur geprüft, ob der Wert dieses Attributes ungleich NULL ist, + der entsprechende Gerätewerte besteht aus dem aktuellen Wärmepumpenstatus und der Heizwassertemperatur.
      • doStatistics < 0 | 1 > -
        - Berechnet statistische Werte: statBoilerGradientHeatUp, statBoilerGradientCoolDown, statBoilerGradientCoolDownMin (Wärmeverlust des Boilers)
      • -
      • allowSetParameter < 0 | 1 > -
        - Die internen Parameter der Wärmepumpensteuerung können nur geändert werden, wenn dieses Attribut auf 1 gesetzt ist.
      • -
      • autoSynchClock <Zeitunterschied> -
        - Die Uhr der Wärmepumpe wird automatisch korrigiert, wenn ein gewisser Zeitunterschied (10 s - 600 s) gegenüber der FHEM Zeit erreicht ist. Zuvor wird die Kompatibilität der Firmware überprüft.
        - (Ein Gerätewert 'delayDeviceTimeCalc' <= 2 s ist auf die internen Berechnungsintervale der Wärmepumpensteuerung zurückzuführen.)
      • -
      • ignoreFirmwareCheck < 0 | 1 > -
        - Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass Wärmepumpen mit ungetester Firmware nicht unabsichtlich beschädigt werden. Wenn dieses Attribute auf 1 gesetzt ist, dann wird der Firmware-Test ignoriert und neue Firmware kann getestet werden. Dieses Attribut wird jedoch ignoriert, wenn die Steuerungs-Firmware bereits als nicht kompatibel berichtet wurde.
      • +
        + Berechnet statistische Werte: statBoilerGradientHeatUp, statBoilerGradientCoolDown, + statBoilerGradientCoolDownMin (Wärmeverlust des Boilers) +
      • allowSetParameter < 0 | 1 > +
        + Die internen Parameter der Wärmepumpensteuerung können + nur geändert werden, wenn dieses Attribut auf 1 gesetzt ist.
      • +
      • autoSynchClock <Zeitunterschied> +
        + Die Uhr der Wärmepumpe wird automatisch korrigiert, wenn ein gewisser Zeitunterschied (10 s - 600 s) + gegenüber der FHEM Zeit erreicht ist. Zuvor wird die Kompatibilität der Firmware überprüft.
        + (Ein Gerätewert 'delayDeviceTimeCalc' <= 2 s ist auf die internen Berechnungsintervale der + Wärmepumpensteuerung zurückzuführen.)
      • +
      • ignoreFirmwareCheck < 0 | 1 > +
        + Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass Wärmepumpen + mit ungetester Firmware nicht unabsichtlich beschädigt werden. Wenn dieses Attribute auf 1 + gesetzt ist, dann wird der Firmware-Test ignoriert und neue Firmware kann getestet werden. + Dieses Attribut wird jedoch ignoriert, wenn die Steuerungs-Firmware bereits als nicht kompatibel berichtet wurde.
      • readingFnAttributes
      -
      -
    =end html_DE diff --git a/fhem/FHEM/70_JSONMETER.pm b/fhem/FHEM/70_JSONMETER.pm index 306c80084..62f029304 100644 --- a/fhem/FHEM/70_JSONMETER.pm +++ b/fhem/FHEM/70_JSONMETER.pm @@ -6,7 +6,8 @@ # # (c) 2014 Torsten Poitzsch < torsten . poitzsch at gmx . de > # -# This module reads data from devices that provide OBIS compatible json pathStrings (e.g. power meters) +# This module reads data from devices that provide OBIS compatible data +# in json format (e.g. power meters) # # 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 @@ -15,7 +16,7 @@ # # The GNU General Public License can be found at # http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textpathString GPL.txt and important notices to the license +# A copy is found in the text file 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, @@ -62,48 +63,48 @@ sub JSONMETER_UpdateAborted($); # Syntax: meterType => port URL-Path ############################################################## my %meterTypes = ( ITF => "80 GetMeasuredValue.cgi" - ,EFR => "80 json.txt" - ); + ,EFR => "80 json.txt" + ); ############################################################## # Syntax: valueType, code, FHEM reading name, statisticType - # valueType: 1=OBISvalue | 2=OBISvalueString | 3=jsonEntryTime | 4=jsonEntry + # valueType: 1=OBISvalue | 2=OBISvalueString | 3=jsonEntryTime | 4=jsonEntry # statisticType: 0=noStatistic | 1=maxMinStatistic | 2=timeStatistic ############################################################## my @jsonFields = ( - [3, "meterType", "meterType", 0] # {"meterId": "0000000061015736", "meterType": "Simplex", "interval": 0, "entry": [ - ,[4, "timestamp", "deviceTime", 0] # {"timestamp": 1389296286, "periodEntries": [ - ,[5, "010000090B00", "deviceTime", 0] # { "obis":"010000090B00","value":"dd.mm.yyyy,hh:mm"} - ,[2, "0.0.0", "meterID", 0] # {"obis": "0.0.0", "scale": 0, "value": 1627477814, "unit": "", "valueString": "0000000061015736" }, - ,[5, "0100000000FF", "meterID", 0] # # { "obis":"0100000000FF","value":"xxxxx"}, - ,[2, "0.2.0", "firmware", 0] # {"obis": "0.2.0", "scale": 0, "value": 0, "unit": "", "valueString": "V320090704" }, - ,[1, "1.7.0", "currentPower", 1] # {"obis": "1.7.0", "scale": 0, "value": 392, "unit": "W", "valueString": "0000392" }, - ,[1, "0100010700FF", "currentPower", 1] # {"obis":"0100010700FF","value":313.07,"unit":"W"}, - ,[1, "0100150700FF", "currentPowerPhase1", 1] # {"obis":"0100150700FF","value":209.40,"unit":"W"}, - ,[1, "0100290700FF", "currentPowerPhase2", 1] # {"obis":"0100290700FF","value":14.27,"unit":"W"}, - ,[1, "01003D0700FF", "currentPowerPhase3", 1] # {"obis":"01003D0700FF","value":89.40,"unit":"W"}, - ,[1, "1.8.0", "powerConsumption", 2] # {"obis": "1.8.0", "scale": 0, "value": 8802276, "unit": "Wh", "valueString": "0008802.276" }, - ,[1, "0101010800FF", "powerConsumption", 2] #{"obis":"0101010800FF","value":41.42,"unit":"kWh" }, - ,[1, "0101010801FF", "powerConsumptionTariff1", 2] # {"obis":"0101010801FF","value":33.53,"unit":"kWh"}, - ,[1, "0101010802FF", "powerConsumptionTariff2", 2] # {"obis":"0101010802FF","value":33.53,"unit":"kWh"}, - ,[1, "0101010803FF", "powerConsumptionTariff3", 2] # {"obis":"0101010803FF","value":33.53,"unit":"kWh"}, - ,[1, "0101010804FF", "powerConsumptionTariff4", 2] # {"obis":"0101010804FF","value":33.53,"unit":"kWh"}, - ,[1, "0101010805FF", "powerConsumptionTariff5", 2] # {"obis":"0101010805FF","value":33.53,"unit":"kWh"}, - ,[1, "010001080080", "powerConsumptionToday", 1] - ,[1, "010001080081", "powerConsumptionYesterday", 1] - ,[1, "010001080082", "powerConsumptionLastWeek", 1] - ,[1, "010001080083", "powerConsumptionLastMonth", 1] - ,[1, "010001080084", "powerConsumptionLastYear", 1] - ,[1, "010002080080", "powerProductionToday", 1] - ,[1, "010002080081", "powerProductionYesterday", 1] - ,[1, "010002080082", "powerProductionLastWeek", 1] - ,[1, "010002080083", "powerProductionLastMonth", 1] - ,[1, "010002080084", "powerProductionLastYear", 1] - ,[1, "0101020800FF", "powerProduction", 2] - ,[1, "010020070000", "voltagePhase1", 1] #{"obis":"010020070000","value":237.06,"unit":"V"}, - ,[1, "010034070000", "voltagePhase2", 1] # {"obis":"010034070000","value":236.28,"unit":"V"}, - ,[1, "010048070000", "voltagePhase3", 1] # {"obis":"010048070000","value":236.90,"unit":"V"}, - ,[1, "01000E070000", "frequency", 1] # {"obis":"01000E070000","value":49.950,"unit":"Hz"} + [3, "meterType", "meterType", 0] # {"meterId": "0000000061015736", "meterType": "Simplex", "interval": 0, "entry": [ + ,[4, "timestamp", "deviceTime", 0] # {"timestamp": 1389296286, "periodEntries": [ + ,[5, "010000090B00", "deviceTime", 0] # { "obis":"010000090B00","value":"dd.mm.yyyy,hh:mm"} + ,[2, "0.0.0", "meterID", 0] # {"obis": "0.0.0", "scale": 0, "value": 1627477814, "unit": "", "valueString": "0000000061015736" }, + ,[5, "0100000000FF", "meterID", 0] # # { "obis":"0100000000FF","value":"xxxxx"}, + ,[2, "0.2.0", "firmware", 0] # {"obis": "0.2.0", "scale": 0, "value": 0, "unit": "", "valueString": "V320090704" }, + ,[1, "1.7.0", "electricityPower", 1] # {"obis": "1.7.0", "scale": 0, "value": 392, "unit": "W", "valueString": "0000392" }, + ,[1, "0100010700FF", "electricityPower", 1] # {"obis":"0100010700FF","value":313.07,"unit":"W"}, + ,[1, "0100150700FF", "electricityPowerPhase1", 1] # {"obis":"0100150700FF","value":209.40,"unit":"W"}, + ,[1, "0100290700FF", "electricityPowerPhase2", 1] # {"obis":"0100290700FF","value":14.27,"unit":"W"}, + ,[1, "01003D0700FF", "electricityPowerPhase3", 1] # {"obis":"01003D0700FF","value":89.40,"unit":"W"}, + ,[1, "1.8.0", "electricityConsumed", 2] # {"obis": "1.8.0", "scale": 0, "value": 8802276, "unit": "Wh", "valueString": "0008802.276" }, + ,[1, "0101010800FF", "electricityConsumed", 2] #{"obis":"0101010800FF","value":41.42,"unit":"kWh" }, + ,[1, "0101010801FF", "electricityConsumedTariff1", 2] # {"obis":"0101010801FF","value":33.53,"unit":"kWh"}, + ,[1, "0101010802FF", "electricityConsumedTariff2", 2] # {"obis":"0101010802FF","value":33.53,"unit":"kWh"}, + ,[1, "0101010803FF", "electricityConsumedTariff3", 2] # {"obis":"0101010803FF","value":33.53,"unit":"kWh"}, + ,[1, "0101010804FF", "electricityConsumedTariff4", 2] # {"obis":"0101010804FF","value":33.53,"unit":"kWh"}, + ,[1, "0101010805FF", "electricityConsumedTariff5", 2] # {"obis":"0101010805FF","value":33.53,"unit":"kWh"}, + ,[1, "010001080080", "electricityConsumedToday", 1] + ,[1, "010001080081", "electricityConsumedYesterday", 1] + ,[1, "010001080082", "electricityConsumedLastWeek", 1] + ,[1, "010001080083", "electricityConsumedLastMonth", 1] + ,[1, "010001080084", "electricityConsumedLastYear", 1] + ,[1, "010002080080", "electricityProducedToday", 1] + ,[1, "010002080081", "electricityProducedYesterday", 1] + ,[1, "010002080082", "electricityProducedLastWeek", 1] + ,[1, "010002080083", "electricityProducedLastMonth", 1] + ,[1, "010002080084", "electricityProducedLastYear", 1] + ,[1, "0101020800FF", "electricityPowerOutput", 2] + ,[1, "010020070000", "electricityVoltagePhase1", 1] #{"obis":"010020070000","value":237.06,"unit":"V"}, + ,[1, "010034070000", "electricityVoltagePhase2", 1] # {"obis":"010034070000","value":236.28,"unit":"V"}, + ,[1, "010048070000", "electricityVoltagePhase3", 1] # {"obis":"010048070000","value":236.90,"unit":"V"}, + ,[1, "01000E070000", "electricityFrequency", 1] # {"obis":"01000E070000","value":49.950,"unit":"Hz"} ); ############################################################## @@ -120,10 +121,13 @@ JSONMETER_Initialize($) $hash->{GetFn} = "JSONMETER_Get"; $hash->{AttrFn} = "JSONMETER_Attr"; $hash->{AttrList} = "disable:0,1 " - ."doStatistics:0,1 " - ."pathString " - ."port " - .$readingFnAttributes; + ."doStatistics:0,1 " + ."pathString " + ."port " + ."electricityTariff " + ."electricityTariff1 " + ."electricityTariff2 " + .$readingFnAttributes; } # end JSONMETER_Initialize @@ -143,19 +147,19 @@ JSONMETER_Define($$) my $typeStr; if ($type eq "file") { - return "Usage: define JSONMETER url [interval]" if (@args >4); - $interval = $args[3] if(int(@args) == 4); + return "Usage: define JSONMETER url [interval]" if (@args >4); + $interval = $args[3] if(int(@args) == 4); } else { - return "Usage: define JSONMETER [interval]" if(@args <4); - $host = $args[3]; - $interval = $args[4] if(int(@args) == 5); + return "Usage: define JSONMETER [interval]" if(@args <4); + $host = $args[3]; + $interval = $args[4] if(int(@args) == 5); } $interval = 10 if( $interval < 10 && $interval != 0); if ($type ne "url" && $type ne "file") { - $typeStr = $meterTypes{$type}; + $typeStr = $meterTypes{$type}; return "Unknown type '$type': use url|file|". join ("|", keys(%meterTypes)) unless $typeStr; my @typeAttr = split / /, $typeStr; $hash->{PORT} = $typeAttr[0]; @@ -201,21 +205,21 @@ JSONMETER_Undefine($$) sub ########################################## JSONMETER_Attr($@) { - my ($cmd,$name,$aName,$aVal) = @_; - # $cmd can be "del" or "set" - # $name is device name - # aName and aVal are Attribute name and value - if ($cmd eq "set") { - if ($aName eq "1allowSetParameter") { - eval { qr/$aVal/ }; - if ($@) { - Log3 $name, 3, "JSONMETER: Invalid allowSetParameter in attr $name $aName $aVal: $@"; - return "Invalid allowSetParameter $aVal"; - } - } - } - - return undef; + my ($cmd,$name,$aName,$aVal) = @_; + # $cmd can be "del" or "set" + # $name is device name + # aName and aVal are Attribute name and value + if ($cmd eq "set") { + if ($aName eq "1allowSetParameter") { + eval { qr/$aVal/ }; + if ($@) { + Log3 $name, 3, "JSONMETER: Invalid allowSetParameter in attr $name $aName $aVal: $@"; + return "Invalid allowSetParameter $aVal"; + } + } + } + + return undef; } # JSONMETER_Attr ende @@ -233,19 +237,19 @@ JSONMETER_Set($$@) } elsif($cmd eq 'restartJsonAnalysis') { $hash->{fhem}{jsonInterpreter} = ""; - $hash->{LOCAL} = 1; + $hash->{LOCAL} = 1; JSONMETER_GetUpdate($hash); $hash->{LOCAL} = 0; return undef; } elsif($cmd eq 'INTERVAL' && int(@_)==4 ) { - $val = 10 if( $val < 10 ); - $hash->{INTERVAL}=$val; - return "$name: Polling interval set to $val seconds."; + $val = 10 if( $val < 10 ); + $hash->{INTERVAL}=$val; + return "$name: Polling interval set to $val seconds."; } my $list = "statusRequest:noArg" - ." restartJsonAnalysis:noArg" - ." INTERVAL:slider,0,10,600"; + ." restartJsonAnalysis:noArg" + ." INTERVAL:slider,0,10,600"; return "Unknown argument $cmd, choose one of $list"; } # end JSONMETER_Set @@ -258,10 +262,10 @@ JSONMETER_Get($@) my $result; if ($cmd eq "jsonFile") { - $result = JSONMETER_GetJsonFile $name; - my @a = split /\|/, $result; - my $message = decode_base64($a[2]); - return $message; + $result = JSONMETER_GetJsonFile $name; + my @a = split /\|/, $result; + my $message = decode_base64($a[2]); + return $message; } my $list = "jsonFile:noArg"; @@ -273,82 +277,82 @@ JSONMETER_Get($@) sub ########################################## JSONMETER_GetUpdate($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $type = $hash->{deviceType}; - - - if(!$hash->{LOCAL} && $hash->{INTERVAL} > 0) { - RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "JSONMETER_GetUpdate", $hash, 1); - } + my ($hash) = @_; + my $name = $hash->{NAME}; + my $type = $hash->{deviceType}; + + + if(!$hash->{LOCAL} && $hash->{INTERVAL} > 0) { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "JSONMETER_GetUpdate", $hash, 1); + } - - if ( ( $type eq "url" || $type eq "file" ) && ! defined($attr{$name}{"pathString"}) ) - { - Log3 $name,2,"$name - Error reading device: Please define the attribute 'pathString'"; - $hash->{STATE} = "pathString missing"; - return "$name|0|"."Error reading device: Please define the attribute 'pathString'."; - } - - $hash->{helper}{RUNNING_PID} = BlockingCall("JSONMETER_GetJsonFile", $name, "JSONMETER_UpdateDone", 10,"JSONMETER_UpdateAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + + if ( ( $type eq "url" || $type eq "file" ) && ! defined($attr{$name}{"pathString"}) ) + { + Log3 $name,2,"$name - Error reading device: Please define the attribute 'pathString'"; + $hash->{STATE} = "pathString missing"; + return "$name|0|"."Error reading device: Please define the attribute 'pathString'."; + } + + $hash->{helper}{RUNNING_PID} = BlockingCall("JSONMETER_GetJsonFile", $name, "JSONMETER_UpdateDone", 10,"JSONMETER_UpdateAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); } sub ########################################## JSONMETER_GetJsonFile ($) { - my ($name) = @_; - my $returnStr; + my ($name) = @_; + my $returnStr; my $hash = $defs{$name}; - my $type = $hash->{deviceType}; - my $ip = ""; - $ip = $hash->{HOST} if defined $hash->{HOST}; - - my $urlPath = ""; - $urlPath = $hash->{urlPath} if defined $hash->{urlPath}; + my $type = $hash->{deviceType}; + my $ip = ""; + $ip = $hash->{HOST} if defined $hash->{HOST}; + + my $urlPath = ""; + $urlPath = $hash->{urlPath} if defined $hash->{urlPath}; - return "$name|0|".encode_base64("Error: deviceType '$type' Please define the attribute 'pathString' first.") - if ($type eq "url" || $type eq "file") && ! defined($attr{$name}{"pathString"}); + return "$name|0|".encode_base64("Error: deviceType '$type' Please define the attribute 'pathString' first.") + if ($type eq "url" || $type eq "file") && ! defined($attr{$name}{"pathString"}); - my $pathString = ""; - $pathString = $attr{$name}{"pathString"} if defined($attr{$name}{"pathString"}); - - my $port = 80; - $port = $hash->{PORT} if defined $hash->{PORT}; - $port = $attr{$name}{"port"} if $type eq "url" && defined($attr{$name}{"port"}); - $hash->{PORT} = $port if $type ne "file"; - - - if ( $type eq "file") - { - $returnStr = JSONMETER_ReadFromFile $name."|".$pathString; - } - else - { - $returnStr = JSONMETER_ReadFromUrl $name."|".$ip."|".$port."|".$urlPath.$pathString; - } - return $returnStr; + my $pathString = ""; + $pathString = $attr{$name}{"pathString"} if defined($attr{$name}{"pathString"}); + + my $port = 80; + $port = $hash->{PORT} if defined $hash->{PORT}; + $port = $attr{$name}{"port"} if $type eq "url" && defined($attr{$name}{"port"}); + $hash->{PORT} = $port if $type ne "file"; + + + if ( $type eq "file") + { + $returnStr = JSONMETER_ReadFromFile $name."|".$pathString; + } + else + { + $returnStr = JSONMETER_ReadFromUrl $name."|".$ip."|".$port."|".$urlPath.$pathString; + } + return $returnStr; } sub ########################################## JSONMETER_ReadFromFile($) { - my ($string) = @_; - my ($name, $pathString) = split /\|/, $string; + my ($string) = @_; + my ($name, $pathString) = split /\|/, $string; - Log3 $name, 4, "$name: Open file '$pathString'"; - if (open(IN, "<" . $pathString)) { - my $message = join " ", ; - # my @file = ; - close(IN); - Log3 $name, 4, "$name: Close file"; - $message = encode_base64($message,""); - return "$name|1|$message" ; - } else { - Log3 $name, 2, "$name Error: Cannot open file $pathString: $!"; - return "$name|0|Error: Cannot open file";; - } + Log3 $name, 4, "$name: Open file '$pathString'"; + if (open(IN, "<" . $pathString)) { + my $message = join " ", ; + # my @file = ; + close(IN); + Log3 $name, 4, "$name: Close file"; + $message = encode_base64($message,""); + return "$name|1|$message" ; + } else { + Log3 $name, 2, "$name Error: Cannot open file $pathString: $!"; + return "$name|0|Error: Cannot open file";; + } } # end JSONMETER_ReadFromFile @@ -362,42 +366,42 @@ JSONMETER_ReadFromUrl($) my $buf ; my $message ; - Log3 $name, 4, "$name: opening socket to host $ip port $port" ; + Log3 $name, 4, "$name: opening socket to host $ip port $port" ; - my $socket = new IO::Socket::INET ( - PeerAddr => $ip, - PeerPort => $port, - Proto => 'tcp', - Reuse => 0, - Timeout => 9 - ); - if (!$socket) { - Log3 $name, 1, "$name Error: Could not open connection to ip $ip port $port"; - return "$name|0|Can't connect to ip $ip port $port"; - } + my $socket = new IO::Socket::INET ( + PeerAddr => $ip, + PeerPort => $port, + Proto => 'tcp', + Reuse => 0, + Timeout => 9 + ); + if (!$socket) { + Log3 $name, 1, "$name Error: Could not open connection to ip $ip port $port"; + return "$name|0|Can't connect to ip $ip port $port"; + } - if (defined ($socket) and $socket and $socket->connected()) - { - print $socket "GET /$pathString HTTP/1.0\r\n\r\n"; - Log3 $name, 4, "$name: Get json file from http://$ip:$port/$pathString"; - $socket->autoflush(1); - while ((read $socket, $buf, 1024) > 0) - { - $message .= $buf; - } - Log3 $name, 5, "$name: received:\n $message"; - $socket->close(); - Log3 $name, 4, "$name: Socket closed"; - $message = encode_base64($message,""); - - return "$name|1|$message" ; + if (defined ($socket) and $socket and $socket->connected()) + { + print $socket "GET /$pathString HTTP/1.0\r\n\r\n"; + Log3 $name, 4, "$name: Get json file from http://$ip:$port/$pathString"; + $socket->autoflush(1); + while ((read $socket, $buf, 1024) > 0) + { + $message .= $buf; + } + Log3 $name, 5, "$name: received:\n $message"; + $socket->close(); + Log3 $name, 4, "$name: Socket closed"; + $message = encode_base64($message,""); + + return "$name|1|$message" ; - } else { - Log3 $name, 2, "$name: Cannot open socket to $ip:$port/$pathString"; - - return "$name|0|Error: Cannot open socket to $ip:$port/$pathString"; - - } + } else { + Log3 $name, 2, "$name: Cannot open socket to $ip:$port/$pathString"; + + return "$name|0|Error: Cannot open socket to $ip:$port/$pathString"; + + } } # end JSONMETER_ReadFromUrl @@ -415,121 +419,121 @@ JSONMETER_UpdateDone($) if ( $a[1] == 1 ){ - my $message = decode_base64($a[2]); - $message =~ s/\s/ /g; - $message =~ s/\n/ /g; + my $message = decode_base64($a[2]); + $message =~ s/\s/ /g; + $message =~ s/\n/ /g; - readingsBeginUpdate($hash); + readingsBeginUpdate($hash); my @fields=split(/\{/,$message); # JSON in einzelne Felder zerlegen - - my $jsonInterpreter = ""; + + my $jsonInterpreter = ""; #################################### # ANALYSE once: Find all known obis codes in the first run and store in the item no, # value type and reading name in the jsonInterpreter #################################### if ( $hash->{fhem}{jsonInterpreter} eq "" ) { - Log3 $name, 3, "$name: Analyse JSON pathString for known readings"; - foreach my $f (@jsonFields) - { - for(my $i=0; $i<=$#fields; $i++) - { - if ($$f[0] == 1 || $$f[0] == 5) { - if ($fields[$i] =~ /"obis".*?:.*?"$$f[1]".*?[,}]/ && $fields[$i] =~ /"value"/) { - $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3]"; - Log3 $name,4,"$name: OBIS code \"$$f[1]\" will be stored in $$f[2]"; - } - } elsif ($$f[0] == 2) { - if ($fields[$i] =~ /"obis".*?:.*?"$$f[1]".*?[,}]/ && $fields[$i] =~ /"valueString"/) { - $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3]"; - Log3 $name,4,"$name: OBIS code \"$$f[1]\" will be stored in $$f[2]"; - } - } elsif ($$f[0] == 3) { - if ($fields[$i] =~ /"$$f[1]".*?:.*?[,}]/) { - $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3] $$f[1]"; - Log3 $name,4,"$name: Property \"$$f[1]\" will be stored in $$f[2]"; - } - } elsif ($$f[0] == 4) { - if ($fields[$i] =~ /"$$f[1]".*?:.*?\d*.*?[,}]/) { - $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3] $$f[1]"; - Log3 $name,4,"$name: Property \"$$f[1]\" will be stored in $$f[2]"; - } - } - } - } - if ($jsonInterpreter ne "") { - Log3 $name, 3, "$name: Store results of JSON analysis for next device readings"; - $jsonInterpreter = substr $jsonInterpreter, 1; - $hash->{fhem}{jsonInterpreter} = $jsonInterpreter; - } else { - Log3 $name, 2, "$name: Could not interpret the JSON pathString => please contact FHEM community" if $jsonInterpreter eq ""; - } - } else { - $jsonInterpreter = $hash->{fhem}{jsonInterpreter} if exists $hash->{fhem}{jsonInterpreter}; - } - + Log3 $name, 3, "$name: Analyse JSON pathString for known readings"; + foreach my $f (@jsonFields) + { + for(my $i=0; $i<=$#fields; $i++) + { + if ($$f[0] == 1 || $$f[0] == 5) { + if ($fields[$i] =~ /"obis".*?:.*?"$$f[1]".*?[,}]/ && $fields[$i] =~ /"value"/) { + $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3]"; + Log3 $name,4,"$name: OBIS code \"$$f[1]\" will be stored in $$f[2]"; + } + } elsif ($$f[0] == 2) { + if ($fields[$i] =~ /"obis".*?:.*?"$$f[1]".*?[,}]/ && $fields[$i] =~ /"valueString"/) { + $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3]"; + Log3 $name,4,"$name: OBIS code \"$$f[1]\" will be stored in $$f[2]"; + } + } elsif ($$f[0] == 3) { + if ($fields[$i] =~ /"$$f[1]".*?:.*?[,}]/) { + $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3] $$f[1]"; + Log3 $name,4,"$name: Property \"$$f[1]\" will be stored in $$f[2]"; + } + } elsif ($$f[0] == 4) { + if ($fields[$i] =~ /"$$f[1]".*?:.*?\d*.*?[,}]/) { + $jsonInterpreter .= "|$i $$f[0] $$f[2] $$f[3] $$f[1]"; + Log3 $name,4,"$name: Property \"$$f[1]\" will be stored in $$f[2]"; + } + } + } + } + if ($jsonInterpreter ne "") { + Log3 $name, 3, "$name: Store results of JSON analysis for next device readings"; + $jsonInterpreter = substr $jsonInterpreter, 1; + $hash->{fhem}{jsonInterpreter} = $jsonInterpreter; + } else { + Log3 $name, 2, "$name: Could not interpret the JSON pathString => please contact FHEM community" if $jsonInterpreter eq ""; + } + } else { + $jsonInterpreter = $hash->{fhem}{jsonInterpreter} if exists $hash->{fhem}{jsonInterpreter}; + } + #################################### # INTERPRETE AND STORE # use the previously filled jsonInterpreter to extract the correct values #################################### - my @a = split /\|/, $jsonInterpreter; - Log3 $name, 4, "$name: Extract ".($#a+1)." readings from ".($#fields+1)." json parts"; - foreach (@a) { - Log3 $name, 5, "$name: Handle $_"; - my @b = split / /, $_ ; - if ($b[1] == 1) { - if ($fields[$b[0]] =~ /"value".*?:(.*?)[,\}]/ ) { - $value = $1; - $value =~ s/^\s+|\s+$//g; - Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; - readingsBulkUpdate($hash,$b[2],$value); - } else { - Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; - } - } elsif ($b[1] == 5) { - if ($fields[$b[0]] =~ /"value".*?:.*?"(.*?)".*?[,}]/ ) { - $value = $1; - Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; - readingsBulkUpdate($hash,$b[2],$value); - } else { - Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; - } - } elsif ($b[1] == 2) { - if ($fields[$b[0]] =~ /"valueString".*?:.*?"(.*?)".*?[,}]/ ) { - $value = $1; - Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; - readingsBulkUpdate($hash,$b[2],$value); - } else { - Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; - } - } elsif ($b[1] == 3) { - if ($fields[$b[0]] =~ /"$b[4]".*?:.*?"(.*?)".*?[,}]/ ) { - $value = $1; - Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; - readingsBulkUpdate($hash, $b[2], $value); - } else { - Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; - } - } elsif ($b[1] == 4) { - if ($fields[$b[0]] =~ /"$b[4]".*?:(.*?)[,}]/ ) { - $value = $1; - $value =~ s/^\s+|\s+$//g; - Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; - $value = strftime "%Y-%m-%d %H:%M:%S", localtime($value); - readingsBulkUpdate($hash, $b[2], $value); - } else { - Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; - } - } - } + my @a = split /\|/, $jsonInterpreter; + Log3 $name, 4, "$name: Extract ".($#a+1)." readings from ".($#fields+1)." json parts"; + foreach (@a) { + Log3 $name, 5, "$name: Handle $_"; + my @b = split / /, $_ ; + if ($b[1] == 1) { + if ($fields[$b[0]] =~ /"value".*?:(.*?)[,\}]/ ) { + $value = $1; + $value =~ s/^\s+|\s+$//g; + Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; + readingsBulkUpdate($hash,$b[2],$value); + } else { + Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; + } + } elsif ($b[1] == 5) { + if ($fields[$b[0]] =~ /"value".*?:.*?"(.*?)".*?[,}]/ ) { + $value = $1; + Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; + readingsBulkUpdate($hash,$b[2],$value); + } else { + Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; + } + } elsif ($b[1] == 2) { + if ($fields[$b[0]] =~ /"valueString".*?:.*?"(.*?)".*?[,}]/ ) { + $value = $1; + Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; + readingsBulkUpdate($hash,$b[2],$value); + } else { + Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; + } + } elsif ($b[1] == 3) { + if ($fields[$b[0]] =~ /"$b[4]".*?:.*?"(.*?)".*?[,}]/ ) { + $value = $1; + Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; + readingsBulkUpdate($hash, $b[2], $value); + } else { + Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; + } + } elsif ($b[1] == 4) { + if ($fields[$b[0]] =~ /"$b[4]".*?:(.*?)[,}]/ ) { + $value = $1; + $value =~ s/^\s+|\s+$//g; + Log3 $name, 4, "$name: value $value for reading $b[2] extracted from '$fields[$b[0]]'"; + $value = strftime "%Y-%m-%d %H:%M:%S", localtime($value); + readingsBulkUpdate($hash, $b[2], $value); + } else { + Log3 $name, 4, "$name: Could not extract value for reading $b[2] from '$fields[$b[0]]'"; + } + } + } - readingsBulkUpdate($hash,"state","Connected"); + readingsBulkUpdate($hash,"state","Connected"); readingsEndUpdate($hash,1); DoTrigger($hash->{NAME}, undef) if ($init_done); } else { - readingsSingleUpdate($hash,"state",$a[2],1); + readingsSingleUpdate($hash,"state",$a[2],1); } return undef; @@ -559,75 +563,83 @@ JSONMETER_UpdateAborted($)
    that provides OBIS compliant data in JSON format on a webserver or on the FHEM file system.
    + It assumes normally, that the structur of the JSON data do not change. +
     
    + Define
      -
    • define <name> JSONMETER <deviceType> [<ip address>] [poll-interval] -
      + define <name> JSONMETER <deviceType> [<ip address>] [poll-interval] +
      Example: define powermeter JSONMETER ITF 192.168.178.20 300 -
      +
      If the pool interval is omitted, it is set to 300 (seconds). Smallest possible value is 10. -
      - With 0 it will only update on "manual" request. -
    • <deviceType> -
      - Used to define the path and port to extract the json file. -
      - The attribute 'pathString' can be used to add login information to the URL-path of predefined devices. -
      - Set
      + Set
      • INTERVAL <polling interval>
        - Polling interval in seconds
      • + Polling interval in seconds
      • statusRequest
        Update device information
      • restartJsonAnalysis
        Restarts the analysis of the json file for known readings (compliant to the OBIS standard). -
        - This analysis happens normally only once if readings have been found.
      • +
        + This analysis happens normally only once if readings have been found.
      -
      + Get
      • jsonFile -
        - extracts and shows the json file
      • +
        + extracts and shows the json data +
      • jsonAnalysis +
        + extracts the json data and shows the result of the analysis

      -
      Attributes
      • doStatistics < 0 | 1 > -
        - Calculates statistic values - not implemented yet
      • -
      • pathString <string> -
          -
        • if deviceType = 'file': specifies the local file name and path
        • -
        • if deviceType = 'url': specifies the url path
        • -
        • other deviceType: can be used to add login information to the url path of predefined devices
        • -
        -
      • port <number> -
        - Specifies the IP port for the deviceType 'url' (default is 80)
      • +
        + Calculates statistic values - not implemented yet +
      • alwaysAnalyse < 0 | 1 > +
        + Repeats by each update the json analysis - use if structur of json data changes - not implemented yet
      • +
      • pathString <string> +
          +
        • if deviceType = 'file': specifies the local file name and path
        • +
        • if deviceType = 'url': specifies the url path
        • +
        • other deviceType: can be used to add login information to the url path of predefined devices
        • +
        +
      • +
      • port <number> +
        + Specifies the IP port for the deviceType 'url' (default is 80)
      • readingFnAttributes
    @@ -639,83 +651,81 @@ JSONMETER_UpdateAborted($)

    JSONMETER

      - Dieses Modul liest Daten von Messgeräten (z.B. Stromzähler/Energiezähler, Gaszähler oder Wärmezähler, so genannte Smartmeter), + Dieses Modul liest Daten von Messgeräten (z.B. Stromzähler, Gaszähler oder Wärmezähler, so genannte Smartmeter),
      welche OBIS kompatible Daten im JSON-Format auf einem Webserver oder auf dem FHEM-Dateisystem zur Verfügung stellen.
       
      + Define
        -
      • define <name> JSONMETER <Gerätetyp> [<IP-Adresse>] [Abfrageinterval] -
        + define <name> JSONMETER <Gerätetyp> [<IP-Adresse>] [Abfrageinterval] +
        Beispiel: define Stromzaehler JSONMETER ITF 192.168.178.20 300 -
        +
        Wenn das Abfrage-Interval nicht angegeben ist, wird es auf 300 (Sekunden) gesetzt. Der kleinste mögliche Wert ist 30. -
        - Bei 0 kann die Geräteabfrage nur manuell gestartet werden. -
      • Gerätetyp -
        - Definiert den Pfad und den Port, um die JSON-Datei zu einzulesen. -
        - Mit dem Attribute 'pathString' können Login Information an den URL-Pfad von vordefinierten Geräte angehangen werden. -
          -
        • ITF - Eintarifzähler von N-ENERGY Netz GmbH (Industrietechnik Fröschle)
        • -
        • EFR - Stromzähler von EON, N-ENERGY, EnBW -
          -       Die Login Information werden über das Attribute 'pathstring' angegeben. -
          -       ?LogName=Benutzer&LogPSWD=Passwort
        • -
        • url - benutzt die URL, welche durch das Attribut 'pathString' und 'port' definiert wird.
        • -
        • file - benutzt die Datei, welche durch das Attribut 'pathString' definiert wird (im FHEM Dateisystem)
        • - -
        -
      • +
        + Bei 0 kann die Geräteabfrage nur manuell gestartet werden. +
      • Gerätetyp +
        + Definiert den Pfad und den Port, um die JSON-Datei zu einzulesen. +
        + Mit dem Attribute 'pathString' können Login Information an den URL-Pfad von vordefinierten Geräte angehangen werden. +
          +
        • ITF - Eintarifzähler von N-ENERGY Netz GmbH (Industrietechnik Fröschle)
        • +
        • EFR - Stromzähler von EON, N-ENERGY, EnBW +
          +       Die Login Information werden über das Attribute 'pathstring' angegeben. +
          +       ?LogName=Benutzer&LogPSWD=Passwort
        • +
        • url - benutzt die URL, welche durch das Attribut 'pathString' und 'port' definiert wird.
        • +
        • file - benutzt die Datei, welche durch das Attribut 'pathString' definiert wird (im FHEM Dateisystem)
        • +
        +

      - Set
      + Set
        -
      • INTERVAL <Abfrageinterval> -
        - Abfrageinterval in Sekunden
      • -
      • statusRequest -
        - Aktualisieren der Gerätewerte
      • +
      • INTERVAL <Abfrageinterval> +
        + Abfrageinterval in Sekunden
      • +
      • statusRequest +
        + Aktualisieren der Gerätewerte
      • restartJsonAnalysis -
        - Neustart der Analyse der json-Datei zum Auffinden bekannter Gerätewerte (kompatibel zum OBIS Standard). -
        - Diese Analysie wird normaler Weise nur einmal durchgeführt, wenn Gerätewerte gefunden wurden.
      • +
        + Neustart der Analyse der json-Datei zum Auffinden bekannter Gerätewerte (kompatibel zum OBIS Standard). +
        + Diese Analysie wird normaler Weise nur einmal durchgeführt, wenn Gerätewerte gefunden wurden.
      -
      - Get + + Get
      • jsonFile -
        - Liest die JSON-Datei ein und zeigt sie an.
      • +
        + Liest die JSON-Datei ein und zeigt sie an.

      -
      Attributes
        -
      • doStatistics < 0 | 1 > -
        - Berechnet statistische Werte - noch nicht implementiert
      • -
      • pathString <Zeichenkette> -
          -
        • Gerätetyp 'file': definiert den lokalen Dateinamen und -pfad
        • -
        • Gerätetyp 'url': Definiert den URL-Pfad
        • -
        • Andere: Kann benutzt werden um Login-Information zum URL Pfad von vordefinerten Geräten hinzuzufügen -
          - e.g.
        • -
        -
      • port <Nummer> -
        - Beim Gerätetyp 'url' kann hier der URL-Port festgelegt werden (standardmässig 80)
      • +
      • doStatistics < 0 | 1 > +
        + Berechnet statistische Werte - noch nicht implementiert
      • +
      • pathString <Zeichenkette> +
          +
        • Gerätetyp 'file': definiert den lokalen Dateinamen und -pfad
        • +
        • Gerätetyp 'url': Definiert den URL-Pfad
        • +
        • Andere: Kann benutzt werden um Login-Information zum URL Pfad von vordefinerten Geräten hinzuzufügen
        • +
        +
      • +
      • port <Nummer> +
        + Beim Gerätetyp 'url' kann hier der URL-Port festgelegt werden (standardmässig 80)
      • readingFnAttributes