Summe aller Einschaltzeiten eines Gerätes

Aus FHEMWiki

Dieses Beispiel ermittelt aus der Datenbank die Einschaltdauer eines Kühlschrankes (über einen Zwischenstecker HM-ES-PMSw1-Pl) innerhalb der letzten 24 Stunden. Wie üblich kann der Auswertungszeitraum über die Time-spezifischen Attribute abgeändert werden.

Es wird angenommen, dass "Aus" = 0 ist und "Ein" ein von 0 abweichender Wert (1 oder eine andere Zahl).

Es sind zwei Komponenten notwendig, ein DbRep-Device und eine Subroutine "switchontime" in der 99_myUtils. Diese Auswertungsroutine kann abweichend davon auch in einer eigenen 99_xxxUtils.pm, z.B. 99_myOntimeUtils.pm, eingebaut werden. Diese Datei ist in das FHEM-Verzeichnis zu kopieren (üblicherweise /opt/fhem/FHEM).

DbRep-Device

Das DbRep-Device wird entsprechend dieser Raw-Definition angelegt:

define Rep.powerOnTime DbRep LogDB
attr Rep.powerOnTime aggregation no
attr Rep.powerOnTime comment Ermittlung der Anschaltdauer (Switch-on Time) eines Gerätes (z.B. Kühlschrank) \
innerhalb der letzten 24 Stunden.\
Es wird die Auswertungsroutine "switchontime" im Attribut "userExitFn" benötigt \
(in 99_myOntimeUtils.pm enthalten).\
\
Start erfolgt mit:      "set <;Name>; fetchrows history"
attr Rep.powerOnTime devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen
attr Rep.powerOnTime device eg.az.fridge_Pwr
attr Rep.powerOnTime event-on-update-reading state,switch_on_time_sec
attr Rep.powerOnTime reading power
attr Rep.powerOnTime room DbLog
attr Rep.powerOnTime timeDiffToNow 86400
attr Rep.powerOnTime userExitFn switchontime
attr Rep.powerOnTime verbose 2

Natürlich ist das angegeben DbLog-Device (LogDB) in der Definition, sowie die Attribute device und reading den eigenen Gegebenheiten anzupassen. Wichtig ist die Angabe:

attr Rep.powerOnTime userExitFn switchontime

was die Aktivierung der userExit-Funktion bzw. den Aufruf der Auswertungsroutine "switchontime" bewirkt.

Die Daten in der Datenbank befinden sich im Device "eg.az.fridge_Pwr" und dem Reading "power". Daraus resultiert dass nach einem

set <Name> fetchrows history

Readings mit folgenden Muster generiert werden.

set <Name> fetchrows history
2017-11-22_16-26-50__eg.az.fridge_Pwr__power  0  2017-11-22 18:33:50 
2017-11-22_16-29-54__eg.az.fridge_Pwr__power  0  2017-11-22 18:33:50 
2017-11-22_16-32-43__eg.az.fridge_Pwr__power  57.83  2017-11-22 18:33:50 
2017-11-22_16-35-18__eg.az.fridge_Pwr__power  51.09  2017-11-22 18:33:50 
2017-11-22_16-37-39__eg.az.fridge_Pwr__power  50.13  2017-11-22 18:33:50 
2017-11-22_16-39-45__eg.az.fridge_Pwr__power  50.17  2017-11-22 18:33:50 
2017-11-22_16-42-40__eg.az.fridge_Pwr__power  49.59  2017-11-22 18:33:50 
2017-11-22_16-45-22__eg.az.fridge_Pwr__power  48.57  2017-11-22 18:33:50 
2017-11-22_16-47-48__eg.az.fridge_Pwr__power  47.48  2017-11-22 18:33:50 
2017-11-22_16-50-01__eg.az.fridge_Pwr__power  46.97  2017-11-22 18:33:50 
2017-11-22_16-53-03__eg.az.fridge_Pwr__power  46.02  2017-11-22 18:33:50 
2017-11-22_16-53-05__eg.az.fridge_Pwr__power  11.09  2017-11-22 18:33:50 
2017-11-22_16-53-13__eg.az.fridge_Pwr__power  0  2017-11-22 18:33:50
2017-11-22_18-14-05__eg.az.fridge_Pwr__power  0 2017-11-22 18:33:50 
2017-11-22_18-16-39__eg.az.fridge_Pwr__power  0 2017-11-22 18:33:50 
2017-11-22_18-18-58__eg.az.fridge_Pwr__power  0 2017-11-22 18:33:50 
2017-11-22_18-21-03__eg.az.fridge_Pwr__power  0 2017-11-22 18:33:50 
2017-11-22_18-23-58__eg.az.fridge_Pwr__power  0 2017-11-22 18:33:50 
2017-11-22_18-26-08__eg.az.fridge_Pwr__power  56.37 2017-11-22 18:33:50 
2017-11-22_18-26-38__eg.az.fridge_Pwr__power  43.22 2017-11-22 18:33:50 
2017-11-22_18-29-04__eg.az.fridge_Pwr__power  50.92 2017-11-22 18:33:50 
2017-11-22_18-31-16__eg.az.fridge_Pwr__power  50.12 2017-11-22 18:33:50

Auswertungsroutine "switchontime"

Die nachfolgende Auswertungsroutine wird z.B. in der Datei 99_myOntimeUtils.pm gespeichert. Der Code ist mit Kommentaren angereichert um den Ablauf der Auswertung verständlich darzustellen und dadurch eine abgewndelte Nutzung in eigenen Projekten zu erleichtern.

Den Code in die Datei kopieren:

############################################################################
# $Id: myUtilsTemplate.pm 7570 2015-01-14 18:31:44Z rudolfkoenig $
#

package main;

use strict;
use warnings;
use POSIX;
use Time::Local;

sub
myOntimeUtils_Initialize($$)
{
  my ($hash) = @_;
}

############################################################################################################
##                           Ermitteln der Einschaltzeiten eines Gerätes                                
############################################################################################################
# Im DbRep-Device ist das Attribut "userExitFn = switchontime" zu setzen.
# Auswertung wird im DbRep gestartet mit:
#
#      set <DbRep-Device> fetchrows history
#
# Die resultierende Switch-on Zeit wird im aufrufenden DbRep-Device in Readings dargestellt:
#      switch_on_time     -> on-Zeit als formatierte Angabe hhh:mm:ss
#      switch_on_time_sec -> on-Zeit als Sekundenwert
#    
# Die Routine ist für alle Readings geeignet die Zahlenwerte als Switch-on/off Zustand beinhalten.
#      Switch-off = 0
#      Swich-off  != 0
#
# Nach der Erstellung jedes einzelnen Readings wird die im Attribut "userExitFn" hinterlegte Funktion
# aufgerufen. Es wird der Devicename des aufrufenden Device, Reading und Wert des Readings übergeben.
# Der Aufrufablauf ist:
# 1. Start: state = running 
# 2. Übermittlung jedes generierten Readings
# 3. Stop: state = done (bzw. nicht running)
#
###########################################################################################################
my ($esb,$ese,$esl);
my $t = 0;                                   # Variable für resultierende Sekunden
my $pattern = "eg.az.fridge_Pwr__power";     # verwendbarer Teilstring des Switch-on/Switch-off Werte 
                                             # enthaltenden Readings (entsprechend anpassen !)

sub switchontime($$$) {
 my ($name,$reading,$value) = @_;
 my $hash = $defs{$name};
 
 if($reading eq "state" && $value eq "running") {
     # der Select wurde gestartet
     Log3 $name, 3, "$name - UserExitFn \"ontime\" has been called."; 
     $t = 0;
     return;
 }
 
 if($reading =~ /$pattern/) {
     # den Timestamp des letzten Payload-Readings unabhängig vom Status
     # (switch-on/switch-off) festhalten
     my $lstr = (split("__",$reading))[0];
     my ($year, $month, $day, $hour, $min, $sec) = ($lstr =~ /(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)/); 
	  
     # Umwandeln in Epochesekunden
     $esl = timelocal($sec, $min, $hour, $day, $month-1, $year-1900); 
 }
 
 if($reading =~ /$pattern/ && $value != 0 && !$esb) {
     # Gerät wurde eingeschaltet
     # Beginn Zeitperiode (Unix-Timestamp) ermitteln
     my $bstr = (split("__",$reading))[0];
     my ($year, $month, $day, $hour, $min, $sec) = ($bstr =~ /(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)/); 
	  
     # Umwandeln in Epochesekunden Beginn
     $esb = timelocal($sec, $min, $hour, $day, $month-1, $year-1900);
     Log3 $name, 4, "$name - Epoche start: $esb (Reading: $reading)"; 
 }
 
 if($reading =~ /$pattern/ && $value == 0 && $esb) {
     # Gerät wurde ausgeschaltet
     # Ende Zeitperiode (Unix-Timestamp) ermitteln
     my $estr = (split("__",$reading))[0];
     my ($year, $month, $day, $hour, $min, $sec) = ($estr =~ /(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)/); 
	  
     # Umwandeln in Epochesekunden Ende
     $ese = timelocal($sec, $min, $hour, $day, $month-1, $year-1900);
     Log3 $name, 4, "$name - Epoche end: $ese (Reading: $reading)"; 
	 
     # Einschaltzeit in Sekunden summieren (Auswerterichtung DESC - neuester TS -> ältester TS) 
     $t = $t + ($esb - $ese);
	 
     Log3 $name, 4, "$name - switch-on time summary: $t seconds"; 
	 
     undef $esb;
     return;
 }
 
 if($reading eq "state" && $value ne "running") {
     # die Selektion ist beendet, Summensekunden in Format hhh:mm:ss umwamdeln
     if ($esb) {
         # am Ende der Auswertung ist Status immer noch switch-on
	 # den Last-Timestamp verwenden
	 $t = $t + ($esb - $esl);
	 undef $esb;
     }
	 
     fhem("setreading $name switch_on_time_sec $t"); 
	 
     my $m = int $t/60;
     my $s = $t - ($m*60);
     my $h = int $m/60;
     $m = $m - ($h*60);
     my $timestring = sprintf("%03d:%02d:%02d",$h,$m,$s);
	 
     fhem("setreading $name switch_on_time $timestring (hhh:mm:ss)"); 
	 
     Log3 $name, 3, "$name - switch-on time was $timestring (hhh:mm:ss)"; 
 }
 
return;
}

1;


Nun ist der Suchpattern des Beispiels in der Zeile:


my $pattern = "eg.az.fridge_Pwr__power";


so abzuändern, dass ein auswertbarer Teilstring des Readingsnamens mit den Switch-on/Switch-off Informationen enthalten ist.

Im Fall dass das Device "Ttim" und das Reading "state" heißt, wäre dies z.B.:

my $pattern = "Ttim__state";

Wenn man sich nicht sicher ist, kann durch die Ausführung von:

set <Name> fetchrows history

geprüft werden welche Readings im DbRep-Device erstellt werden und benutzt den Teilstring nach dem führenden Datum/Zeit-String des Readings. Also zum Beispiel:


Reading: 2017-11-22_11-08-02__eg.az.fridge_Pwr__power -> Teilstring für $pattern: eg.az.fridge_Pwr__power


Nun die Utils-Datei laden mit:

reload 99_myOntimeUtils.pm

oder FHEM Restart.

Die Auswertung wird nun gestartet mit:

zusätzliche Readings history
set Rep.powerOnTime fetchrows history


Es werden zwei zusätzliche Readings im Device Rep.powerOnTime generiert, switch_on_time und switch_on_time_sec. Das Reading switch_on_time_sec ist die summarische "EIN"-Zeit in Sekunden der letzten 24 Stunden und kann gut zur weiteren Auswertung genutzt werden. switch_on_time ist eine in hhh:mm:ss formatierte Angabe des Readingswertes von switch_on_time_sec zur leichteren Lesbarkeit.

Natürlich kann man switch_on_time_sec wiederum loggen um z.B. daraus eine grafische Anzeige zu erstellen, die die Entwicklung der täglichen "EIN"-Zeiten über einen längeren Zeitraum darstellen könnte (z.B. Heizzeiten).

Mit kleinen Abwandlungen könnten zum Beispiel auch die täglichen Raumlüftungszeiten (Fenter "open", "closed") ausgewertet und überwacht werden. Wird die vorgegeben "open"-Zeit unterschritten, könnte ein Alarm ausgelöst werden.

Wie üblich können die Auswertungszeitgrenzen durch die Zeit-spezifischen Attribute timestamp_begin, timestamp_end usw. den Anforderungen entsprechend abgeändert werden.