Summe aller Einschaltzeiten eines Gerätes
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.
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 $
# Beispiel aus https://wiki.fhem.de/wiki/Summe_aller_Einschaltzeiten_eines_Ger%C3%A4tes
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 $t = 0; # Variable für resultierende Sekunden
my $sekunden = 0; # Variable für sekunden des Readings
my $startzeit = 0; # Variable für Beginn des Zeitraums
my $endzeit = 0; # Variable für Ende des Zeitraums
my $letztervalue = 0; # Variable für Value des vorherigen Readings
my $letztersekunden = 0; # Variable für Sekunde des vorherigen Readings
my $initvalue = 0;
my $pattern = "Ttim__state"; # verwendbarer Teilstring des Switch-on/Switch-off Werte
# enthaltenden Readings (entsprechend anpassen !)
my $runde;
sub switchontime($$$)
{
my ($name,$reading,$value) = @_;
my $hash = $defs{$name};
# $value = int $value; #schöner für Anzeigen
if($reading eq "state" && $value eq "running") # der Select wurde gestartet
{
$letztervalue = -1;
$runde = 0;
my ($devn,$devread) = split("__",$pattern); # den aktuellen Zustand des Devices ermitteln
$devread = uc ($devread);
$initvalue = InternalVal("$devn","$devread",99);
if ($initvalue == 99)
{
Log3 $name, 1, "$name - der Patternname scheint falsch zu sein";
}
my $jetzt = TimeNow(); # aktuelle Zeit = Ende des Abfragezeitraums ermitteln
my $lstr = (split("__",$jetzt))[0];
my ($year, $month, $day, $hour, $min, $sec) = ($lstr =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/);
# Umwandeln in Epochesekunden
$endzeit = timelocal($sec, $min, $hour, $day, $month-1, $year-1900);
$startzeit = $endzeit - AttrVal("Rep.powerOnTime","timeDiffToNow",98); # Beginn des Abfragezeitraums ermitteln
Log3 $name, 3, "$name - UserExitFn \"ontime\" has been called.";
($sec,$min,$hour) = localtime($startzeit);
Log3 $name, 3, "Start der Abfrage $startzeit $hour:$min:$sec";
($sec,$min,$hour) = localtime($endzeit);
Log3 $name, 3, "Ende der Abfrage $endzeit $hour:$min:$sec";
$t = 0;
return;
}
$runde = $runde +1;
if($reading =~ /$pattern/) # den Timestamp unabhängig vom Status festhalten
{
my $lstr = (split("__",$reading))[0];
my ($year, $month, $day, $hour, $min, $sec) = ($lstr =~ /(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)/);
# Umwandeln in Epochesekunden
$sekunden = timelocal($sec, $min, $hour, $day, $month-1, $year-1900);
Log3 $name, 4, "t:$t s:$sekunden value:$value $hour:$min:$sec ";
if($letztervalue == -1) # wenn jüngster Wert (erstes reading)
{
if($value != 0) # an
{
$t = $endzeit;
}
}
if ($value == $letztervalue)
{
Log3 $name, 2, "$name - Ein value ist doppelt";
}
if( $value != 0 && $value != $letztervalue) # Gerät wurde eingeschaltet
{
$t = $t - $sekunden;
}
if($value == 0 && $value != $letztervalue) # Gerät wurde ausgeschaltet
{
$t = $t + $sekunden;
}
# Value sichern für endberechnung und doppelte Werte
$letztervalue = $value;
$letztersekunden = $sekunden;
}
if($reading eq "state" && $value ne "running") # die Selektion ist beendet, Summensekunden in Format hhh:mm:ss umwamdeln
{
if ($letztervalue == 0) # am Ende der Auswertung ist Status immer noch switch-on
{
$t = $t - $startzeit;
}
if ($letztervalue == -1) #wenn keine Werte kamen
{
if( $initvalue != 0 ) # Gerät ganze Zeit an
{
$t = AttrVal("Rep.powerOnTime","timeDiffToNow",98);
Log3 $name, 5, "Gerät ganze Zeit an";
}
if($initvalue == 0 ) # Gerät ganze Zeit aus
{
$t = 0;
Log3 $name, 5, "Gerät ganze Zeit an";
}
}
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;
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.