mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-13 23:36:37 +00:00
git-svn-id: https://svn.fhem.de/fhem/trunk@5736 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
6253083598
commit
61b2318d0f
@ -39,7 +39,7 @@ use IO::Socket;
|
||||
use Time::HiRes qw/ time /;
|
||||
use Net::Telnet;
|
||||
|
||||
sub LUXTRONIK2_doStatisticThermalPower ($$$$$$$$);
|
||||
sub LUXTRONIK2_doStatisticThermalPower ($$$$$$$$$);
|
||||
sub LUXTRONIK2_doStatisticMinMax ($$$);
|
||||
sub LUXTRONIK2_doStatisticMinMaxSingle ($$$$);
|
||||
sub LUXTRONIK2_storeReadings ($$$$$$);
|
||||
@ -47,7 +47,7 @@ sub LUXTRONIK2_doStatisticDelta ($$$$$) ;
|
||||
sub LUXTRONIK2_doStatisticDeltaSingle ($$$$$$);
|
||||
|
||||
# Modul Version for remote debugging
|
||||
my $modulVersion = "2014-04-30";
|
||||
my $modulVersion = "2014-05-03";
|
||||
|
||||
#List of firmware versions that are known to be compatible with this modul
|
||||
my $testedFirmware = "#V1.51#V1.54C#V1.60#V1.69#V1.70#";
|
||||
@ -67,8 +67,8 @@ LUXTRONIK2_Initialize($)
|
||||
"allowSetParameter:0,1 ".
|
||||
"autoSynchClock:slider,10,5,300 ".
|
||||
"boilerVolumn ".
|
||||
"heatPumpElectricalPowerFactor ".
|
||||
"heatPumpElectricalPowerWatt:slider,1000,16000,100 ".
|
||||
"heatPumpHotWaterElectricalPowerWatt:slider,1000,16000,100 ".
|
||||
"heatRodElectricalPowerWatt:slider,1000,16000,100 ".
|
||||
"compressor2ElectricalPowerWatt:slider,1000,16000,100 ".
|
||||
"doStatistics:0,1 ".
|
||||
@ -748,6 +748,24 @@ LUXTRONIK2_UpdateDone($)
|
||||
my $returnTemperature = LUXTRONIK2_CalcTemp($a[16]);
|
||||
my $returnTemperatureTarget = LUXTRONIK2_CalcTemp($a[17]);
|
||||
|
||||
my $heatPumpPower = 0;
|
||||
my $heatRodPower = AttrVal($name, "heatRodElectricalPowerWatt", 0);
|
||||
|
||||
#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) )
|
||||
my $thermalPower = 0;
|
||||
# 0=Heizen, 5=Brauchwasser, 7=Abtauen, 16=Durchflussüberwachung
|
||||
if ($a[3] =~ /^(0|5|16)$/ ) {
|
||||
$thermalPower = abs($flowTemperature - $returnTemperature) * $a[19] / 866.65;
|
||||
$heatPumpPower = AttrVal($name, "heatPumpElectricalPowerWatt", -1);
|
||||
$heatPumpPower *= 1 + ($flowTemperature-35) * AttrVal($name, "heatPumpElectricalPowerFactor", 0);
|
||||
}
|
||||
readingsBulkUpdate( $hash, "thermalPower", sprintf "%.1f", $thermalPower);
|
||||
if ($heatPumpPower >-1 ) { readingsBulkUpdate( $hash, "heatPumpElectricalPowerEstimated", sprintf "%.0f", $heatPumpPower); }
|
||||
if ($heatPumpPower > 0) {
|
||||
$cop = $thermalPower * 1000 / $heatPumpPower;
|
||||
readingsBulkUpdate( $hash, "COP", sprintf "%.2f", $cop);
|
||||
}
|
||||
|
||||
# if selected, do all the statistic calculations
|
||||
if ( $doStatistic == 1) {
|
||||
#LUXTRONIK2_doStatisticBoilerHeatUp $hash, $currOpHours, $currHQ, $currTemp, $opState, $target
|
||||
@ -776,9 +794,9 @@ LUXTRONIK2_UpdateDone($)
|
||||
}
|
||||
|
||||
# LUXTRONIK2_doStatisticThermalPower: $hash, $MonitoredOpState, $currOpState, $currHeatQuantity, $currOpHours, $currAmbTemp, $currHeatSourceIn, $TargetTemp
|
||||
$value = LUXTRONIK2_doStatisticThermalPower ($hash, 5, $a[3], $a[37]/10, $a[35], $ambientTemperature, $heatSourceIN,$hotWaterTemperatureTarget);
|
||||
$value = LUXTRONIK2_doStatisticThermalPower ($hash, 5, $a[3], $a[37]/10, $a[35], $ambientTemperature, $heatSourceIN,$hotWaterTemperatureTarget, $heatPumpPower);
|
||||
if ($value ne "") { readingsBulkUpdate($hash,"statThermalPowerBoiler",$value); }
|
||||
$value = LUXTRONIK2_doStatisticThermalPower ($hash, 0, $a[3], $a[36]/10, $a[34], $ambientTemperature, $heatSourceIN, $returnTemperatureTarget);
|
||||
$value = LUXTRONIK2_doStatisticThermalPower ($hash, 0, $a[3], $a[36]/10, $a[34], $ambientTemperature, $heatSourceIN, $returnTemperatureTarget, $heatPumpPower);
|
||||
if ($value ne "") { readingsBulkUpdate($hash,"statThermalPowerHeating",$value); }
|
||||
|
||||
# LUXTRONIK2_doStatisticMinMax $hash, $readingName, $value
|
||||
@ -886,15 +904,15 @@ LUXTRONIK2_UpdateDone($)
|
||||
readingsBulkUpdate( $hash, "heatSourceIN",$heatSourceIN);
|
||||
readingsBulkUpdate( $hash, "heatSourceOUT",LUXTRONIK2_CalcTemp($a[24]));
|
||||
readingsBulkUpdate( $hash, "hotGasTemperature",LUXTRONIK2_CalcTemp($a[26]));
|
||||
|
||||
|
||||
# Operating hours (seconds->hours) and heat quantities
|
||||
# LUXTRONIK2_storeReadings: $hash, $readingName, $value, $factor, $doStatistic, $tariffType
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource1", $a[32], 3600, $doStatistic, 4;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource2", $a[38], 3600, $doStatistic, 4;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource3", $a[39], 3600, $doStatistic, 4;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHeatPump", $a[33], 3600, $doStatistic, 1;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHeating", $a[34], 3600, $doStatistic, 2;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHotWater", $a[35], 3600, $doStatistic, 3;
|
||||
# LUXTRONIK2_storeReadings: $hash, $readingName, $value, $factor, $doStatistic, $electricalPower
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource1", $a[32], 3600, $doStatistic, $heatRodPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource2", $a[38], 3600, $doStatistic, $heatRodPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource3", $a[39], 3600, $doStatistic, $heatRodPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHeatPump", $a[33], 3600, $doStatistic, $heatPumpPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHeating", $a[34], 3600, $doStatistic, $heatPumpPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHoursHotWater", $a[35], 3600, $doStatistic, $heatPumpPower;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHeatQHeating", $a[36], 10, ($a[19] !~ /no/ ? $doStatistic : 0), 0;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHeatQHotWater", $a[37], 10, ($a[19] !~ /no/ ? $doStatistic : 0), 0;
|
||||
LUXTRONIK2_storeReadings $hash, "counterHeatQTotal", $a[36] + $a[37], 10, ($a[19] !~ /no/ ? $doStatistic : 0), 0;
|
||||
@ -923,24 +941,7 @@ LUXTRONIK2_UpdateDone($)
|
||||
$value = "unbekannt (".$a[31].")" unless $value;
|
||||
readingsBulkUpdate($hash,"typeHeatpump",$value);
|
||||
|
||||
# Monitored operation: 5=Hot Water, 0=Heating
|
||||
my $devicePower=0;
|
||||
if ($a[3] == 5) {$devicePower = AttrVal($hash->{NAME}, "heatPumpHotWaterElectricalPowerWatt", 0);}
|
||||
if ($devicePower == 0) {$devicePower = AttrVal($hash->{NAME}, "heatPumpElectricalPowerWatt", 0);}
|
||||
|
||||
#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) )
|
||||
my $thermalPower = 0;
|
||||
# 0=Heizen, 5=Brauchwasser, 7=Abtauen, 16=Durchflussüberwachung
|
||||
if ($a[3] =~ /^(0|5|16)$/ ) {
|
||||
$thermalPower = abs($flowTemperature - $returnTemperature) * $a[19] / 866.65;
|
||||
}
|
||||
readingsBulkUpdate( $hash, "thermalPower", sprintf "%.1f", $thermalPower);
|
||||
if ($devicePower > 0) {
|
||||
$cop = $thermalPower *1000 / $devicePower;
|
||||
readingsBulkUpdate( $hash, "COP", sprintf "%.2f", $cop);
|
||||
}
|
||||
|
||||
# Solar
|
||||
# Solar
|
||||
if ($a[50] !~ /no/) {readingsBulkUpdate($hash, "solarCollectorTemperature", LUXTRONIK2_CalcTemp($a[50]));}
|
||||
if ($a[51] !~ /no/) {readingsBulkUpdate($hash, "solarBufferTemperature", LUXTRONIK2_CalcTemp($a[51]));}
|
||||
if ($a[52] !~ /no/) {readingsBulkUpdate($hash, "counterHoursSolar", sprintf("%.1f", $a[52]/3600));}
|
||||
@ -958,7 +959,7 @@ LUXTRONIK2_UpdateDone($)
|
||||
$value = "$opStateHeatPump1 $opStateHeatPump2 - $opStateHeatPump3";
|
||||
if ($thermalPower != 0) {
|
||||
$value .= " (".sprintf ("%.1f", $thermalPower)." kW";
|
||||
if ($devicePower>0) {$value .= ", COP: ".sprintf ("%.2f", $cop);}
|
||||
if ($heatPumpPower>0) {$value .= ", COP: ".sprintf ("%.2f", $cop);}
|
||||
$value .= ")"; }
|
||||
readingsBulkUpdate($hash, "state", $value);
|
||||
|
||||
@ -1201,32 +1202,40 @@ LUXTRONIK2_checkFirmware ($)
|
||||
|
||||
# Calculate heat-up gradients of boiler based on hotWaterTemperature and counterHeatQHeating
|
||||
sub ########################################
|
||||
LUXTRONIK2_doStatisticThermalPower ($$$$$$$$)
|
||||
LUXTRONIK2_doStatisticThermalPower ($$$$$$$$$)
|
||||
{
|
||||
my ($hash, $MonitoredOpState, $currOpState, $currHeatQuantity, $currOpHours, $currAmbTemp, $currHeatSourceIn, $targetTemp) = @_;
|
||||
my ($hash, $MonitoredOpState, $currOpState, $currHeatQuantity, $currOpHours, $currAmbTemp, $currHeatSourceIn, $targetTemp, $electricalPower) = @_;
|
||||
my @last = split / /, $hash->{fhem}{"statThermalPowerOpState_".$MonitoredOpState} || "1";
|
||||
my $saveCurrent = 0;
|
||||
my $returnStr = "";
|
||||
my $value1;
|
||||
my $value2;
|
||||
my $value3;
|
||||
$last[3] += $currAmbTemp;
|
||||
$last[4] += $currHeatSourceIn;
|
||||
$last[5]++;
|
||||
|
||||
# Monitored operation: 5=Hot Water, 0=Heating
|
||||
my $devicePower = 0;
|
||||
if ($MonitoredOpState == 5) {$devicePower = AttrVal($hash->{NAME}, "heatPumpHotWaterElectricalPowerWatt", 0);}
|
||||
if ($devicePower == 0) {$devicePower = AttrVal($hash->{NAME}, "heatPumpElectricalPowerWatt", 0);}
|
||||
my $save = 0;
|
||||
|
||||
if ($last[0] != $MonitoredOpState && $currOpState == $MonitoredOpState ) {
|
||||
if ( $last[0] != $MonitoredOpState && $currOpState == $MonitoredOpState ) {
|
||||
# Save start values at the beginning of the monitored operation (5=Hot Water, 0=Heating)
|
||||
$saveCurrent = 1;
|
||||
$save = 1;
|
||||
$last[0] = $currOpState;
|
||||
$last[1] = $currHeatQuantity;
|
||||
$last[2] = $currOpHours;
|
||||
$last[3] = $currAmbTemp;
|
||||
$last[4] = $currHeatSourceIn;
|
||||
$last[5] = 1;
|
||||
$last[6] = $targetTemp;
|
||||
$last[7] = $electricalPower;
|
||||
|
||||
} elsif ($last[0] == $MonitoredOpState && ($currOpState == $MonitoredOpState || $currOpState == 16) ) { #16=Durchflussüberwachung
|
||||
# Store intermediate values as long as the correct opMode runs
|
||||
$save = 1;
|
||||
$last[3] += $currAmbTemp;
|
||||
$last[4] += $currHeatSourceIn;
|
||||
$last[5]++;
|
||||
$last[7] += $electricalPower;
|
||||
|
||||
} elsif ($last[0] == $MonitoredOpState && $currOpState != $MonitoredOpState && $currOpState != 16 ) { #16=Durchflussüberwachung
|
||||
# Do statistics at the end of the monitored operation
|
||||
$saveCurrent = 1;
|
||||
# Do statistics at the end of the monitored operation if it run at least 9.5 minutes
|
||||
$save = 1;
|
||||
$last[0] = $currOpState;
|
||||
$value2 = ($currOpHours - $last[2])/60;
|
||||
if ($value2 > 9.5) {
|
||||
$value1 = $last[3] / $last[5];
|
||||
@ -1239,19 +1248,16 @@ LUXTRONIK2_doStatisticThermalPower ($$$$$$$$)
|
||||
$returnStr .= " thP: " . sprintf "%.1f", $value3;
|
||||
$returnStr .= " DQ: " . sprintf "%.1f", $value1;
|
||||
$returnStr .= " t: " . sprintf "%.0f", $value2;
|
||||
if ($devicePower>0) {
|
||||
$value1 = $value3 *1000 / $devicePower;
|
||||
if ($last[7]>0) {
|
||||
$value1 = $value3 *1000 / $last[7] * $last[5];;
|
||||
$returnStr .= " COP: " . sprintf "%.2f", $value1;
|
||||
}
|
||||
if ($last[6] > $targetTemp) { $returnStr .= " tTStart: " . sprintf "%.1f", $last[6]; }
|
||||
}
|
||||
}
|
||||
if ($saveCurrent == 1) {
|
||||
$last[0] = $currOpState;
|
||||
$last[1] = $currHeatQuantity;
|
||||
$last[2] = $currOpHours;
|
||||
$hash->{fhem}{"statThermalPowerOpState_".$MonitoredOpState} = join( " ", @last);
|
||||
}
|
||||
|
||||
if ($save == 1) { $hash->{fhem}{"statThermalPowerOpState_".$MonitoredOpState} = join( " ", @last);}
|
||||
|
||||
return $returnStr;
|
||||
}
|
||||
|
||||
@ -1534,7 +1540,7 @@ LUXTRONIK2_doStatisticMinMaxSingle ($$$$)
|
||||
sub ########################################
|
||||
LUXTRONIK2_storeReadings($$$$$$)
|
||||
{
|
||||
my ($hash, $readingName, $value, $factor, $doStatistics, $tariffType) = @_;
|
||||
my ($hash, $readingName, $value, $factor, $doStatistics, $electricalPower) = @_;
|
||||
|
||||
if ($value eq "no" || $value == 0 ) { return; }
|
||||
|
||||
@ -1542,15 +1548,15 @@ LUXTRONIK2_storeReadings($$$$$$)
|
||||
|
||||
$readingName =~ s/counter//;
|
||||
|
||||
# LUXTRONIK2_doStatisticDelta: $hash, $readingName, $value, $factor, $tariffType
|
||||
if ( $doStatistics == 1) { LUXTRONIK2_doStatisticDelta $hash, "stat".$readingName, $value, $factor, $tariffType; }
|
||||
# LUXTRONIK2_doStatisticDelta: $hash, $readingName, $value, $factor, $electricalPower
|
||||
if ( $doStatistics == 1) { LUXTRONIK2_doStatisticDelta $hash, "stat".$readingName, $value, $factor, $electricalPower; }
|
||||
}
|
||||
|
||||
# Calculates deltas for day, month and year
|
||||
sub ########################################
|
||||
LUXTRONIK2_doStatisticDelta ($$$$$)
|
||||
{
|
||||
my ($hash, $readingName, $value, $factor, $tariffType) = @_;
|
||||
my ($hash, $readingName, $value, $factor, $electricalPower) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $dummy;
|
||||
my $result;
|
||||
@ -1599,19 +1605,13 @@ LUXTRONIK2_doStatisticDelta ($$$$$)
|
||||
|
||||
my $activeTariff = ReadingsVal($name,"activeTariff",0);
|
||||
|
||||
if ( $tariffType != 0 ) {
|
||||
if ( $electricalPower != 0 ) {
|
||||
my $readingNamePower = $readingName;
|
||||
$readingNamePower =~ s/Hours/Electricity/ ;
|
||||
my $powerValue;
|
||||
if ( $tariffType == 1 || $tariffType == 2 ) { $powerValue = AttrVal($name,"heatPumpElectricalPowerWatt",0);
|
||||
} elsif ( $tariffType == 3 ) {
|
||||
$powerValue = AttrVal($name,"heatPumpHotWaterElectricalPowerWatt",0);
|
||||
if ($powerValue == 0) { $powerValue = AttrVal($name,"heatPumpElectricalPowerWatt",0); }
|
||||
} elsif ( $tariffType == 4 ) { $powerValue = AttrVal($name,"heatRodElectricalPowerWatt",0); }
|
||||
if ($powerValue > 0) {
|
||||
if ($electricalPower > 0) {
|
||||
foreach (1,2,3,4,5,6,7,8,9) {
|
||||
if ( $previousTariff == $_ ) {
|
||||
LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower."Tariff".$_, $deltaValue * $powerValue, $factor, $periodSwitch, $showDate);
|
||||
LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower."Tariff".$_, $deltaValue * $electricalPower, $factor, $periodSwitch, $showDate);
|
||||
} elsif ($activeTariff == $_ || ($periodSwitch > 0 && exists($hash->{READINGS}{$readingNamePower . "Tariff".$_}))) {
|
||||
LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower."Tariff".$_, 0, $factor, $periodSwitch, $showDate);
|
||||
}
|
||||
@ -1638,7 +1638,8 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$)
|
||||
@curr = split / /, $hash->{READINGS}{".".$readingName}{VAL} || "";
|
||||
} else {
|
||||
$curr[1] = 0; $curr[3] = 0; $curr[5] = 0;
|
||||
$curr[7] = strftime "%Y-%m-%d_%H:%M:%S", localtime(); # start
|
||||
if ($showDate>5) {$curr[7] = strftime "%Y-%m-%d_%H:%M:%S", localtime();} # start
|
||||
else {$curr[7] = strftime "%Y-%m-%d", localtime();} # start
|
||||
}
|
||||
|
||||
# get statistic values of previous period
|
||||
@ -1799,10 +1800,10 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$)
|
||||
Logging and visualisation of the statistic should be done with readings of type 'stat<i>ReadingName</i><b>Last</b>'.
|
||||
</li><br>
|
||||
<li><code>heatPumpElectricalPowerWatt</code><br>
|
||||
Electrical power of the heat pump to calculated coefficency factor and estimate electrical consumption
|
||||
Electrical power of the heat pump by a flow temperature of 35°C to calculated coefficency factor and estimate electrical consumption
|
||||
</li><br>
|
||||
<li><code>heatPumpHotWaterElectricalPowerWatt</code><br>
|
||||
Electrical power of the heat pump during hot water preparation to calculated coefficency factor and estimate electrical consumption
|
||||
<li><code>heatPumpElectricalPowerFactor</code><br>
|
||||
Change of electrical power consumption per 1 K flow temperature differenz to 35°C (e.g. 2% per 1 K = 0,02)
|
||||
</li><br>
|
||||
<li><code>heatHeatRodElectricalPowerWatt</code><br>
|
||||
Electrical power of the heat rods (2nd heat source) to estimate electrical consumption
|
||||
@ -1927,13 +1928,12 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$)
|
||||
Für grafische Auswertungen können die Werte der Form 'stat<i>ReadingName</i><b>Last</b>' genutzt werden.
|
||||
</li><br>
|
||||
<li><code>heatPumpElectricalPowerWatt</code><br>
|
||||
Betriebsleistung der Wäremepumpe zur Berechung der Arbeitszahl (erzeugte Wärme pro elektrische Energieeinheit)
|
||||
und Abschätzung des elektrischen Verbrauches
|
||||
</li><br>
|
||||
<li><code>heatPumpHotWaterElectricalPowerWatt</code><br>
|
||||
Betriebsleistung der Wäremepumpe während der Warmwasserbereitung zur Berechung der Arbeitszahl (erzeugte Wärme pro elektrische Energieeinheit)
|
||||
Betriebsleistung der Wäremepumpe bei einer Vorlauftemperatur von 35 °C zur Berechung der Arbeitszahl (erzeugte Wärme pro elektrische Energieeinheit)
|
||||
und Abschätzung des elektrischen Verbrauches
|
||||
</li><br>
|
||||
<li><code>heatPumpElectricalPowerFactor</code><br>
|
||||
Änderung der elektrischen Leistungsaufnahme per 1 K Vorlauftemperaturdifferenz zu 35 °C (z.B. 2% pro 1 K = 0,02)<br>
|
||||
</li><br>
|
||||
<li><code>heatHeatRodElectricalPowerWatt</code><br>
|
||||
Betriebsleistung der Heizstäbe zur Abschätzung des elektrischen Verbrauches
|
||||
</li><br>
|
||||
|
503
fhem/FHEM/98_statistics.pm
Normal file
503
fhem/FHEM/98_statistics.pm
Normal file
@ -0,0 +1,503 @@
|
||||
##############################################
|
||||
#
|
||||
# 98_statistic.pm
|
||||
#
|
||||
# Copyright notice
|
||||
#
|
||||
# (c) 2014 Torsten Poitzsch < torsten . poitzsch at gmx . de >
|
||||
# inspired by 98_rain.pm of Andreas Vogt
|
||||
#
|
||||
# This module computes statistic data of and for readings of other modules
|
||||
#
|
||||
# This script is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The GNU General Public License can be found at
|
||||
# http://www.gnu.org/copyleft/gpl.html.
|
||||
# A copy is found in the 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,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# This copyright notice MUST APPEAR in all copies of the script!
|
||||
#
|
||||
##############################################################################
|
||||
#
|
||||
# define <name> statistics <regexp>
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::Local;
|
||||
|
||||
sub statistics_doStatisticMinMax ($$$);
|
||||
|
||||
# Modul Version for remote debugging
|
||||
my $modulVersion = "2014-04-29";
|
||||
|
||||
##############################################################
|
||||
# Syntax: deviceType, readingName, statisticType
|
||||
# statisticType: 0=noStatistic | 1=maxMinAvgStatistic | 2=integralTimeStatistic | 3=onOffTimeCount
|
||||
##############################################################
|
||||
my @knownDeviceReadings = (
|
||||
["CUL_WS", "humidity", 1]
|
||||
,["CUL_WS", "temperature", 1]
|
||||
,["KS300", "humidity", 1]
|
||||
,["KS300", "temperature", 1]
|
||||
,["KS300", "wind", 1]
|
||||
,["KS300", "rain", 2]
|
||||
,["FBDECT", "energy", 2]
|
||||
,["FBDECT", "power", 1]
|
||||
,["FBDECT", "voltage", 1]
|
||||
);
|
||||
##############################################################
|
||||
|
||||
sub ##########################################
|
||||
statistics_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "statistics_Define";
|
||||
$hash->{NotifyFn} = "statistics_Notify";
|
||||
|
||||
$hash->{NotifyOrderPrefix} = "10-"; # Want to be called before the rest
|
||||
$hash->{AttrList} = "disable:0,1 "
|
||||
."DayChangeTime "
|
||||
."CorrectionValue "
|
||||
.$readingFnAttributes;
|
||||
}
|
||||
|
||||
##########################
|
||||
sub
|
||||
statistics_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "Usage: define <name> statistics <devicename-regexp> [prefix]"
|
||||
if(3>@a || @a>4);
|
||||
|
||||
my $name = $a[0];
|
||||
my $devname = $a[2];
|
||||
|
||||
if (@a == 4) {$hash->{PREFIX} = $a[3];}
|
||||
else {$hash->{PREFIX} = "stat";}
|
||||
|
||||
eval { "Hallo" =~ m/^$devname$/ };
|
||||
return "Bad regexp: $@" if($@);
|
||||
$hash->{DEV_REGEXP} = $devname;
|
||||
|
||||
$hash->{STATE} = "active";
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
##########################
|
||||
sub
|
||||
statistics_Notify($$)
|
||||
{
|
||||
my ($hash, $dev) = @_;
|
||||
my $hashName = $hash->{NAME};
|
||||
my $devName = $dev->{NAME};
|
||||
my $devType = $dev->{TYPE};
|
||||
|
||||
return "" if(AttrVal($hashName, "disable", undef));
|
||||
|
||||
# Return if the notifying device is not monitored
|
||||
return "" if(!defined($hash->{DEV_REGEXP}));
|
||||
my $regexp = $hash->{DEV_REGEXP};
|
||||
return "" if($devName !~ m/^($regexp)$/);
|
||||
|
||||
my $output = $devName." (".$devType.")" ;
|
||||
my $max = int(@{$dev->{CHANGED}});
|
||||
my $readingName;
|
||||
my $value;
|
||||
# Loop through all known device types and readings
|
||||
foreach my $f (@knownDeviceReadings)
|
||||
{
|
||||
$readingName = $$f[1];
|
||||
# notifing device type is known and the device has also the known reading
|
||||
if ($$f[0] eq $devType && exists ($dev->{READINGS}{$readingName})) {
|
||||
if ($$f[2] == 1) { statistics_doStatisticMinMax ($hash, $dev, $readingName);}
|
||||
}
|
||||
}
|
||||
|
||||
# Record device as monitored
|
||||
my $monReadingName = "monitoredDevices".$devType;
|
||||
my $monReadingValue = ReadingsVal($hashName,$monReadingName,"");
|
||||
my $temp = '^'.$devName.'$|^'.$devName.',|,'.$devName.'$|,'.$devName.',';
|
||||
if ($monReadingValue !~ /$temp/) {
|
||||
if($monReadingValue eq "") { $monReadingValue = $devName;}
|
||||
else {$monReadingValue .= ",".$devName;}
|
||||
readingsSingleUpdate($hash,$monReadingName,$monReadingValue,0);
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Calculates single MaxMin Values and informs about end of day and month
|
||||
sub ########################################
|
||||
statistics_doStatisticMinMax ($$$)
|
||||
{
|
||||
my ($hash, $dev, $readingName) = @_;
|
||||
my $dummy;
|
||||
|
||||
my $lastReading;
|
||||
my $lastSums;
|
||||
my @newReading;
|
||||
|
||||
my $yearLast;
|
||||
my $monthLast;
|
||||
my $dayLast;
|
||||
my $dayNow;
|
||||
my $monthNow;
|
||||
my $yearNow;
|
||||
|
||||
my $value = $dev->{READINGS}{$readingName}{VAL};
|
||||
my $prefix = $hash->{PREFIX};
|
||||
|
||||
# Determine date of last and current reading
|
||||
if (exists($dev->{READINGS}{$prefix.ucfirst($readingName)."Day"}{TIME})) {
|
||||
($yearLast, $monthLast, $dayLast) = $dev->{READINGS}{$prefix.ucfirst($readingName)."Day"}{TIME} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/;
|
||||
} else {
|
||||
($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime;
|
||||
$yearLast += 1900;
|
||||
$monthLast ++;
|
||||
}
|
||||
($dummy, $dummy, $dummy, $dayNow, $monthNow, $yearNow) = localtime;
|
||||
$yearNow += 1900;
|
||||
$monthNow ++;
|
||||
|
||||
# Daily Statistic
|
||||
#statistics_doStatisticMinMaxSingle: $hash, $readingName, $value, $saveLast
|
||||
statistics_doStatisticMinMaxSingle $hash, $dev, $readingName."Day", $value, ($dayNow != $dayLast);
|
||||
|
||||
# Monthly Statistic
|
||||
#statistics_doStatisticMinMaxSingle: $hash, $readingName, $value, $saveLast
|
||||
statistics_doStatisticMinMaxSingle $hash, $dev, $readingName."Month", $value, ($monthNow != $monthLast);
|
||||
|
||||
# Yearly Statistic
|
||||
#statistics_doStatisticMinMaxSingle: $hash, $readingName, $value, $saveLast
|
||||
statistics_doStatisticMinMaxSingle $hash, $dev, $readingName."Year", $value, ($yearNow != $yearLast);
|
||||
|
||||
return ;
|
||||
|
||||
}
|
||||
|
||||
# Calculates single MaxMin Values and informs about end of day and month
|
||||
sub ########################################
|
||||
statistics_doStatisticMinMaxSingle ($$$$$)
|
||||
{
|
||||
my ($hash, $dev, $readingName, $value, $saveLast) = @_;
|
||||
my $result;
|
||||
my $hiddenReadingName = ".".$dev->{NAME}.".".$readingName;
|
||||
|
||||
my $statReadingName = $hash->{PREFIX};
|
||||
$statReadingName .= ucfirst($readingName);
|
||||
|
||||
my $lastReading = $dev->{READINGS}{$statReadingName}{VAL} || "";
|
||||
|
||||
# Initializing
|
||||
if ( $lastReading eq "" ) {
|
||||
my $since = strftime "%Y-%m-%d_%H:%M:%S", localtime();
|
||||
$result = "Count: 1 Sum: $value ShowDate: 1";
|
||||
readingsSingleUpdate($hash, $hiddenReadingName, $result,0);
|
||||
$result = "Min: $value Avg: $value Max: $value (since: $since )";
|
||||
readingsSingleUpdate($dev, $statReadingName, $result,0);
|
||||
|
||||
# Calculations
|
||||
} else {
|
||||
my @a = split / /, $hash->{READINGS}{$hiddenReadingName}{VAL}; # Internal values
|
||||
my @b = split / /, $lastReading;
|
||||
# Do calculations
|
||||
$a[1]++; # Count
|
||||
$a[3] += $value; # Sum
|
||||
if ($value < $b[1]) { $b[1]=$value; } # Min
|
||||
$b[3] = sprintf "%.0f" , $a[3] / $a[1]; # Avg
|
||||
if ($value > $b[5]) { $b[5]=$value; } # Max
|
||||
|
||||
# in case of period change, save "last" values and reset counters
|
||||
if ($saveLast) {
|
||||
$result = "Min: $b[1] Avg: $b[3] Max: $b[5]";
|
||||
if ($a[5] == 1) { $result .= " (since: $b[7] )"; }
|
||||
readingsSingleUpdate($dev, $statReadingName . "Last", $lastReading,0);
|
||||
$a[1] = 1; $a[3] = $value; $a[5] = 0;
|
||||
$b[1] = $value; $b[3] = $value; $b[5] = $value;
|
||||
}
|
||||
# Store internal calculation values
|
||||
$result = "Count: $a[1] Sum: $a[3] ShowDate: $a[5]";
|
||||
readingsSingleUpdate($hash, $hiddenReadingName, $result,0);
|
||||
# Store visible Reading
|
||||
$result = "Min: $b[1] Avg: $b[3] Max: $b[5]";
|
||||
if ($a[5] == 1) { $result .= " (since: $b[7] )"; }
|
||||
readingsSingleUpdate($dev, $statReadingName, $result,0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
# Calculates deltas for day, month and year
|
||||
sub ########################################
|
||||
statistics_doStatisticDelta ($$$$$)
|
||||
{
|
||||
my ($hash, $readingName, $value, $special, $activeTariff) = @_;
|
||||
my $dummy;
|
||||
my $result;
|
||||
|
||||
my $deltaValue;
|
||||
my $previousTariff;
|
||||
my $showDate;
|
||||
|
||||
# Determine if time period switched (day, month, year)
|
||||
# Get deltaValue and Tariff of previous call
|
||||
my $periodSwitch = 0;
|
||||
my $yearLast; my $monthLast; my $dayLast; my $hourLast; my $hourNow; my $dayNow; my $monthNow; my $yearNow;
|
||||
if (exists($hash->{READINGS}{"." . $readingName . "Before"})) {
|
||||
($yearLast, $monthLast, $dayLast, $hourLast) = ($hash->{READINGS}{"." . $readingName . "Before"}{TIME} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d)/);
|
||||
$yearLast -= 1900;
|
||||
$monthLast --;
|
||||
($dummy, $deltaValue, $dummy, $previousTariff, $dummy, $showDate) = split / /, $hash->{READINGS}{"." . $readingName . "Before"}{VAL} || "";
|
||||
$deltaValue = $value - $deltaValue;
|
||||
} else {
|
||||
($dummy, $dummy, $hourLast, $dayLast, $monthLast, $yearLast) = localtime;
|
||||
$deltaValue = 0;
|
||||
$previousTariff = 0;
|
||||
$showDate = 8;
|
||||
}
|
||||
($dummy, $dummy, $hourNow, $dayNow, $monthNow, $yearNow) = localtime;
|
||||
|
||||
if ($yearNow != $yearLast) { $periodSwitch = 4; }
|
||||
elsif ($monthNow != $monthLast) { $periodSwitch = 3; }
|
||||
elsif ($dayNow != $dayLast) { $periodSwitch = 2; }
|
||||
elsif ($hourNow != $hourLast) { $periodSwitch = 1; }
|
||||
|
||||
# Determine if "since" value has to be shown in current and last reading
|
||||
if ($periodSwitch == 4) {
|
||||
if ($showDate == 1) { $showDate = 0; } # Do not show the "since:" value for year changes anymore
|
||||
if ($showDate >= 2) { $showDate = 1; } # Shows the "since:" value for the first year change
|
||||
}
|
||||
if ($periodSwitch >= 3){
|
||||
if ($showDate == 3) { $showDate = 2; } # Do not show the "since:" value for month changes anymore
|
||||
if ($showDate >= 4) { $showDate = 3; } # Shows the "since:" value for the first month change
|
||||
}
|
||||
if ($periodSwitch >= 2){
|
||||
if ($showDate == 5) { $showDate = 4; } # Do not show the "since:" value for day changes anymore
|
||||
if ($showDate >= 6) { $showDate = 5; } # Shows the "since:" value for the first day change
|
||||
}
|
||||
if ($periodSwitch >= 1){
|
||||
if ($showDate == 7) { $showDate = 6; } # Do not show the "since:" value for day changes anymore
|
||||
if ($showDate >= 8) { $showDate = 7; } # Shows the "since:" value for the first hour change
|
||||
}
|
||||
|
||||
# statistics_doStatisticDeltaSingle; $hash, $readingName, $deltaValue, $special, $periodSwitch, $showDate, $firstCall
|
||||
statistics_doStatisticDeltaSingle ($hash, $readingName, $deltaValue, $special, $periodSwitch, $showDate);
|
||||
|
||||
foreach (1,2,3,4,5,6,7,8,9) {
|
||||
if ( $previousTariff == $_ ) {
|
||||
statistics_doStatisticDeltaSingle ($hash, $readingName."Tariff".$_, $deltaValue, 0, $periodSwitch, $showDate);
|
||||
} elsif ($activeTariff == $_ || ($periodSwitch > 0 && exists($hash->{READINGS}{$readingName . "Tariff".$_}))) {
|
||||
statistics_doStatisticDeltaSingle ($hash, $readingName."Tariff".$_, 0, 0 , $periodSwitch, $showDate);
|
||||
}
|
||||
}
|
||||
|
||||
# Hidden storage of current values for next call(before values)
|
||||
$result = "Value: $value Tariff: $activeTariff ShowDate: $showDate ";
|
||||
readingsBulkUpdate($hash, ".".$readingName."Before", $result);
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
sub ########################################
|
||||
statistics_doStatisticDeltaSingle ($$$$$$)
|
||||
{
|
||||
my ($hash, $readingName, $deltaValue, $special, $periodSwitch, $showDate) = @_;
|
||||
my $dummy;
|
||||
my $result;
|
||||
|
||||
# get existing statistic reading
|
||||
my @curr;
|
||||
if (exists($hash->{READINGS}{$readingName}{VAL})) {
|
||||
@curr = split / /, $hash->{READINGS}{$readingName}{VAL} || "";
|
||||
if ($curr[0] eq "Day:") { $curr[9]=$curr[7]; $curr[7]=$curr[5]; $curr[5]=$curr[3]; $curr[3]=$curr[1]; $curr[1]=0; }
|
||||
} else {
|
||||
$curr[1] = 0; $curr[3] = 0; $curr[5] = 0; $curr[7] = 0;
|
||||
$curr[9] = strftime "%Y-%m-%d_%H:%M:%S", localtime(); # start
|
||||
}
|
||||
|
||||
# get statistic values of previous period
|
||||
my @last;
|
||||
if ($periodSwitch >= 1) {
|
||||
if (exists ($hash->{READINGS}{$readingName."Last"})) {
|
||||
@last = split / /, $hash->{READINGS}{$readingName."Last"}{VAL};
|
||||
if ($last[0] eq "Day:") { $last[9]=$last[7]; $last[7]=$last[5]; $last[5]=$last[3]; $last[3]=$last[1]; $last[1]="-"; }
|
||||
} else {
|
||||
@last = split / /, "Hour: - Day: - Month: - Year: -";
|
||||
}
|
||||
}
|
||||
|
||||
# Do statistic
|
||||
$curr[1] += $deltaValue;
|
||||
$curr[3] += $deltaValue;
|
||||
$curr[5] += $deltaValue;
|
||||
$curr[7] += $deltaValue;
|
||||
|
||||
# If change of year, change yearly statistic
|
||||
if ($periodSwitch == 4){
|
||||
$last[7] = $curr[7];
|
||||
$curr[7] = 0;
|
||||
if ($showDate == 1) { $last[9] = $curr[9]; }
|
||||
}
|
||||
|
||||
# If change of month, change monthly statistic
|
||||
if ($periodSwitch >= 3){
|
||||
$last[5] = $curr[5];
|
||||
$curr[5] = 0;
|
||||
if ($showDate == 3) { $last[9] = $curr[9];}
|
||||
}
|
||||
|
||||
# If change of day, change daily statistic
|
||||
if ($periodSwitch >= 2){
|
||||
$last[3] = $curr[3];
|
||||
$curr[3] = 0;
|
||||
if ($showDate == 5) {
|
||||
$last[9] = $curr[9];
|
||||
# Next monthly and yearly values start at 00:00 and show only date (no time)
|
||||
$curr[5] = 0;
|
||||
$curr[7] = 0;
|
||||
$curr[9] = strftime "%Y-%m-%d", localtime(); # start
|
||||
}
|
||||
}
|
||||
|
||||
# If change of hour, change hourly statistic
|
||||
if ($periodSwitch >= 1){
|
||||
$last[1] = $curr[1];
|
||||
$curr[1] = 0;
|
||||
if ($showDate == 7) { $last[9] = $curr[9];}
|
||||
}
|
||||
|
||||
# Store visible statistic readings (delta values)
|
||||
$result = "Hour: $curr[1] Day: $curr[3] Month: $curr[5] Year: $curr[7]";
|
||||
if ( $showDate >=2 ) { $result .= " (since: $curr[9] )"; }
|
||||
readingsBulkUpdate($hash,$readingName,$result);
|
||||
|
||||
if ($special == 1) { readingsBulkUpdate($hash,$readingName."Today",$curr[3]) };
|
||||
|
||||
# if changed, store previous visible statistic (delta) values
|
||||
if ($periodSwitch >= 1) {
|
||||
$result = "Hour: $last[1] Day: $last[3] Month: $last[5] Year: $last[7]";
|
||||
if ( $showDate =~ /1|3|5|7/ ) { $result .= " (since: $last[9] )";}
|
||||
readingsBulkUpdate($hash,$readingName."Last",$result);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="statistics"></a>
|
||||
<h3>statistics</h3>
|
||||
<ul>
|
||||
This modul calculates for certain readings of given devices statistical values and adds them to the devices.
|
||||
|
||||
<br>
|
||||
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> statistics <deviceNameRegExp> [Prefix]</code>
|
||||
<br>
|
||||
Beispiel: <code>define Statistik statistics Sensor_.*|Wettersensor</code>
|
||||
<br>
|
||||
<li><code>[Prefix]</code>
|
||||
<br>
|
||||
Optional. Default is <i>stat</i>
|
||||
</li><br>
|
||||
<li><code><DeviceNameRegExp></code>
|
||||
<br>
|
||||
Regular expression of device names. !!! Not the device readings !!!
|
||||
<br>
|
||||
Until now the following device types and readings are analysed:
|
||||
<ul><li><b>CUL_WS:</b> humidity, temperature</li>
|
||||
<li><b>KS300:</b> humidity, temperature, wind, rain</li>
|
||||
<li><b>FBDECT:</b> energy, power, voltage</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<b>Set</b>
|
||||
<ul>not implemented yet
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<b>Get</b>
|
||||
<ul>not implemented yet
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="JSONMETERattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>not implemented yet
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=begin html_DE
|
||||
|
||||
<a name="statistics"></a>
|
||||
<h3>statistics</h3>
|
||||
<ul>
|
||||
Dieses Modul wertet von den angegebenen Geräten bestimmte Werte statistisch aus und fügt sie den jeweiligen Geräten als neue Werte hinzu.
|
||||
|
||||
<br>
|
||||
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <Name> statistics <GeräteNameRegExp> [Prefix]</code>
|
||||
<br>
|
||||
Beispiel: <code>define Statistik statistics Sensor_.*|Wettersensor</code>
|
||||
<br>
|
||||
<li><code>[Prefix]</code>
|
||||
<br>
|
||||
Optional. Standardmässig <i>stat</i>
|
||||
</li><br>
|
||||
<li><code><GeräteNameRegExp></code>
|
||||
<br>
|
||||
Regularer Ausdruck für den Gerätenamen. !!! Nicht die Gerätewerte !!!
|
||||
<br>
|
||||
Derzeit werden folgende Gerätetypen und Gerätewerte ausgewertet:
|
||||
<ul><li><b>CUL_WS:</b> humidity, temperature</li>
|
||||
<li><b>KS300:</b> humidity, temperature, wind, rain</li>
|
||||
<li><b>FBDECT:</b> energy, power, voltage</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<b>Set</b>
|
||||
<ul>noch nicht implementiert
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<b>Get</b>
|
||||
<ul>noch nicht implementiert
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="JSONMETERattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>noch nicht implementiert
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html_DE
|
||||
|
||||
=cut
|
Loading…
x
Reference in New Issue
Block a user