diff --git a/fhem/FHEM/73_ElectricityCalculator.pm b/fhem/FHEM/73_ElectricityCalculator.pm index 9bf3ce7b4..2da18146f 100644 --- a/fhem/FHEM/73_ElectricityCalculator.pm +++ b/fhem/FHEM/73_ElectricityCalculator.pm @@ -44,6 +44,7 @@ package main; use strict; use warnings; +use FHEM::Meta; ###START###### Initialize module ##############################################################################START#### sub ElectricityCalculator_Initialize($) @@ -73,6 +74,7 @@ sub ElectricityCalculator_Initialize($) "Currency:€,£,$ " . "DecimalPlace:3,4,5,6,7 " . $readingFnAttributes; + return FHEM::Meta::InitMod( __FILE__, $hash ); } ####END####### Initialize module ###############################################################################END##### @@ -130,6 +132,19 @@ sub ElectricityCalculator_Define($$$) ### Writing log entry Log3 $name, 5, $name. " : ElectricityCalculator - Starting to define module"; + ### Start timer for execution around midnight + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $EpochNextMidnight = timelocal(1, 0, 0, $mday, $mon, $year+1900) + 86400; + InternalTimer($EpochNextMidnight, "ElectricityCalculator_MidnightTimer", $hash, 0); + + ### For debugging purpose only + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - time : " . time(); + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - year : " . $year; + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - mon : " . $mon; + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - day : " . $mday; + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - timelocal : " . timelocal(1, 0, 0, $mday, $mon, $year+1900); + Log3 $name, 5, $name. " : ElectricityCalculator_MidnightTimer - nextMidnight : " . $EpochNextMidnight; + return undef; } ####END####### Activate module after module has been used via fhem command "define" ############################END##### @@ -398,6 +413,125 @@ sub ElectricityCalculator_Set($@) } ####END####### Manipulate reading after "set" command by fhem ##################################################END##### +###START###### Midnight Routine ###############################################################################START#### +sub ElectricityCalculator_MidnightTimer($) +{ + ### Define variables + my ($ElectricityCalcDev) = @_; + my $ElectricityCalcName = $ElectricityCalcDev->{NAME}; + my $RegEx = $ElectricityCalcDev->{REGEXP}; + my ($ElectricityCountName, $ElectricityCountReadingRegEx) = split(":", $RegEx, 2); + my $ElectricityCountDev = $defs{$ElectricityCountName}; + $ElectricityCountReadingRegEx =~ s/[\.\*]//g; + + my @ElectricityCountReadingNameListComplete = keys(%{$ElectricityCountDev->{READINGS}}); + my @ElectricityCountReadingNameListFiltered; + + foreach my $ElectricityCountReadingName (@ElectricityCountReadingNameListComplete) { + if ($ElectricityCountReadingName =~ m[$ElectricityCountReadingRegEx]) { + push(@ElectricityCountReadingNameListFiltered, $ElectricityCountReadingName); + } + } + + + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer__________________________________________________________"; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer : MidnightTimer initiated"; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - RegEx : " . $RegEx; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ElectricityCountName : " . $ElectricityCountName; + #Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ElectricityCountReadList : " . Dumper(@ElectricityCountReadingNameListFiltered); + + + ### Remove internal timer for ElectricityCalculator_MidnightTimer + RemoveInternalTimer($ElectricityCalcDev, "ElectricityCalculator_MidnightTimer"); + + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Looping through every Counter defined by RegEx"; + + foreach my $ElectricityCountReadingName (@ElectricityCountReadingNameListFiltered) { + + # ### Restore Destination of readings + my $ElectricityCalcReadingPrefix = $ElectricityCountName . "_" . $ElectricityCountReadingName; + my $ElectricityCalcReadingDestinationDeviceName = ReadingsVal($ElectricityCalcName, ".ReadingDestinationDeviceName" , "error"); + my $ElectricityCounterReadingValue = ReadingsVal($ElectricityCountName, $ElectricityCountReadingName , "error"); + my $LastUpdateTimestampUnix = ReadingsVal($ElectricityCalcName, "." . $ElectricityCalcReadingPrefix . "_LastUpdateTimestampUnix", 0 ); + + ### Calculate time difference since last update + my $DeltaTimeSinceLastUpdate = time() - $LastUpdateTimestampUnix ; + + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer ___________Looping________________"; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ReadingPrefix : " . $ElectricityCalcReadingPrefix; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - DeviceName : " . $ElectricityCalcReadingDestinationDeviceName; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Timestamp now : " . time(); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Timestamp update : " . $LastUpdateTimestampUnix; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Timestamp Delta : " . $DeltaTimeSinceLastUpdate; + + + ### If the Readings for midnight settings have been provided + if (($ElectricityCalcReadingPrefix ne "error") && ($ElectricityCalcReadingDestinationDeviceName ne "error") && ($LastUpdateTimestampUnix > 0)){ + + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Timestamp update : " . $LastUpdateTimestampUnix; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Timestamp Delta : " . $DeltaTimeSinceLastUpdate; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ReadingPrefix : " . $ElectricityCalcReadingPrefix; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - DeviceName : " . $ElectricityCalcReadingDestinationDeviceName; + + ### If there was no update in the last 24h + if ( $DeltaTimeSinceLastUpdate >= 86400) { + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Last Update : No Update in the last 24h!"; + + } + else { + ### Create Log entries for debugging purpose + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Last Update : There was an Update in the last 24h!"; + } + + #Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ElectricityCalcRDD : \n" . Dumper($ElectricityCalcReadingDestinationDevice); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ElectricityCounter: " . $ElectricityCounterReadingValue; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDaySum : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PowerDaySum", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDayCount : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PowerDayCount", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDayCurrent : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_PowerCurrent", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDayAver : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_PowerDayAver", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDayMax : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_PowerDayMax", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre WFRDayMin : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_PowerDayMin", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre ConsumDay : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_EnergyDay", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre ConsumDayLast : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_EnergyDayLast", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre ConsumCstDay : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_EnergyCostDay", "error"); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Pre ConsumCstDayL : " . ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_EnergyCostDayLast", "error"); + + + if ($ElectricityCounterReadingValue ne "error") { + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Writing Counter : " . $ElectricityCounterReadingValue; + readingsSingleUpdate($ElectricityCountDev, $ElectricityCountReadingName, $ElectricityCounterReadingValue, 1); + } + else { + + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - Writing Counter : Error!"; + } + } + ### If the Readings for midnight settings have not been provided + else { + ### Warning Log entry + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - ERROR - There have no information stored about previous readings. Make sure the counter has been delivering at least 2 values to the Calculator device before next midnight!"; + } + } + + ### Start timer for execution around midnight + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $EpochNextMidnight = timelocal(1, 0, 0, $mday, $mon, $year+1900) + 86400; + InternalTimer($EpochNextMidnight, "ElectricityCalculator_MidnightTimer", $ElectricityCalcDev, 0); + + ### For debugging purpose only + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer _______Looping finished___________"; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - time : " . time(); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - timelocal : " . timelocal(1, 0, 0, $mday, $mon, $year+1900); + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_MidnightTimer - nextMidnight : " . $EpochNextMidnight; +} +####END####### Midnight Routine ################################################################################END##### + + ###START###### Calculate Electricity meter values on changed events ###################################################START#### sub ElectricityCalculator_Notify($$) { @@ -603,9 +737,13 @@ sub ElectricityCalculator_Notify($$) next; } + ### Save Destination of readings into hidden readings + readingsSingleUpdate($ElectricityCalcDev, ".ReadingDestinationDeviceName", $ElectricityCalcReadingDestinationDeviceName, 0); + ### Restore previous Counter and if not available define it with "undef" - my $ElectricityCountReadingTimestampPrevious = ReadingsTimestamp($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PrevRead", undef); - my $ElectricityCountReadingValuePrevious = ReadingsVal($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PrevRead", undef); + my $ElectricityCountReadingTimestampPrevious = ReadingsTimestamp($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PrevRead", undef); + my $ElectricityCountReadingValuePrevious = ReadingsVal($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_PrevRead", undef); + my $ElectricityCountReadingLastChangeDelta = time() - ReadingsVal($ElectricityCalcReadingDestinationDeviceName, "." . $ElectricityCalcReadingPrefix . "_LastUpdateTimestampUnix", undef); ### Create Log entries for debugging Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator - ElectricityCountReadingValuePrevious : " . $ElectricityCountReadingValuePrevious; @@ -696,11 +834,15 @@ sub ElectricityCalculator_Notify($$) Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator - Current Reading Value : " . $ElectricityCountReadingValueCurrent; ####### Check whether Initial readings needs to be written - ### Check whether the current value is the first one after change of day = First one after midnight - if ($ElectricityCountReadingTimestampCurrentHour < $ElectricityCountReadingTimestampPreviousHour) + ### Check whether the current value is the first one after change of day = First one after midnight or if last update is older than 1 day + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_Notify ElectricityCountReadTimeCurHour : " . $ElectricityCountReadingTimestampCurrentHour; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_Notify ElectricityCountReadTimePrevHour : " . $ElectricityCountReadingTimestampPreviousHour; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator_Notify ElectricityCountReadTimeRelDelta : " . $ElectricityCountReadingLastChangeDelta; + + if (($ElectricityCountReadingTimestampCurrentHour < $ElectricityCountReadingTimestampPreviousHour) || ($ElectricityCountReadingLastChangeDelta > 86400)) { ### Create Log entries for debugging - Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator - First reading of day detected"; + Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator - First reading of day detected OR last reading is older than 24h!"; ### Calculate Electricity energy of previous day € = (Wprevious[kWh] - WcurrentDay[kWh]) my $ElectricityCalcEnergyDayLast = ($ElectricityCountReadingValuePrevious - ReadingsVal($ElectricityCalcReadingDestinationDeviceName, $ElectricityCalcReadingPrefix . "_CounterDay1st", "0")); @@ -791,6 +933,12 @@ sub ElectricityCalculator_Notify($$) my $ElectricityCountReadingValueDelta = sprintf($ElectricityCalcDev->{system}{DecimalPlace}, ($ElectricityCountReadingValueCurrent)) - sprintf($ElectricityCalcDev->{system}{DecimalPlace}, ($ElectricityCountReadingValuePrevious)); Log3 $ElectricityCalcName, 5, $ElectricityCalcName. " : ElectricityCalculator - ElectricityCountReadingValueDelta : " . $ElectricityCountReadingValueDelta; + ### If the value has been changed since the last one + if ($ElectricityCountReadingValueDelta > 0) { + ### Save current Timestamp as UNIX epoch into hash if the + readingsSingleUpdate($ElectricityCalcDev, "." . $ElectricityCalcReadingPrefix . "_LastUpdateTimestampUnix", $ElectricityCountReadingTimestampCurrentRelative, 0); + } + ### Calculate Current Power P = DW/Dt[kWh/s] * 3600[s/h] * 1000 [1/k] / SiPrefixPowerFactor my $ElectricityCalcPowerCurrent = ($ElectricityCountReadingValueDelta / $ElectricityCountReadingTimestampDelta) * 3600 * 1000 / $ElectricityCalcDev->{system}{SiPrefixPowerFactor}; @@ -977,581 +1125,104 @@ sub ElectricityCalculator_Notify($$)

ElectricityCalculator

=end html @@ -1561,583 +1232,159 @@ sub ElectricityCalculator_Notify($$)

ElectricityCalculator

=end html_DE +=for :application/json;q=META.json 73_ElectricityCalculator.pm +{ + "abstract": "Calculates the electrical energy consumption and costs.", + "description": "The ElectricityCalculator Module calculates the electrical energy consumption and costs of one ore more electricity meters.
Tt is not a counter module itself but it requires a regular expression (regex or regexp) in order to know where to retrieve the counting ticks of one or more mechanical or electronic electricity meter.
As soon the module has been defined within the fhem.cfg, the module reacts on every event of the specified counter like myOWDEVICE:counter.* etc.
The ElectricityCalculator module provides several current, historical, statistical values around with respect to one or more electricity meter and creates respective readings.
", + "x_lang": { + "de": { + "abstract": "Berechnet den Energieverbrauch und verbundene Kosten", + "description": "Das ElectricityCalculator Modul berechnet den Verbrauch an elektrischer Energie (Stromverbrauch) und den verbundenen Kosten von einem oder mehreren Elektrizitätszählern.
Es ist kein eigenes Zählermodul sondern benötigt eine Regular Expression (regex or regexp) um das Reading mit den Zählimpulse von einem oder mehreren Electrizitätszählern zu finden.
Sobald das Modul in der fhem.cfg definiert wurde, reagiert das Modul auf jedes durch das regex definierte event wie beispielsweise ein myOWDEVICE:counter.* etc.
Das ElectricityCalculator Modul berechnet augenblickliche, historische statistische und vorhersehbare Werte von einem oder mehreren Elektrizitätszählern und erstellt die entsprechenden Readings.
" + } + }, + "author": [ + "I am the maintainer matthias.deeke@deeke.eu" + ], + "x_fhem_maintainer": [ + "Sailor" + ], + "keywords": [ + "electricity", + "current", + "calculation", + "consumption", + "cost", + "counter" + ], + "prereqs": { + "runtime": { + "requires": { + "FHEM": 5.00918623, + "FHEM::Meta": 0.001006, + "HttpUtils": 0, + "JSON": 0, + "perl": 5.014 + }, + "recommends": { + }, + "suggests": { + } + } + }, + "resources": { + "x_support_community": { + "rss": "https://forum.fhem.de/index.php/topic,57106.msg", + "web": "https://forum.fhem.de/index.php/topic,57106.msg", + "subCommunity" : { + "rss" : "https://forum.fhem.de/index.php/topic,57106.msg", + "title" : "This sub-board will be first contact point", + "web" : "https://forum.fhem.de/index.php/topic,57106.msg" + } + } + }, + "x_support_status": "supported" +} +=end :application/json;q=META.json + =cut \ No newline at end of file