Summe aller Einschaltzeiten eines Gerätes: Unterschied zwischen den Versionen
Krikan (Diskussion | Beiträge) K (kategorisiert) |
Drhirn (Diskussion | Beiträge) K (veraltete "source"-Angaben in "syntaxhighlight" geändert) |
||
(5 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt) | |||
Zeile 6: | Zeile 6: | ||
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). | 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 = | == DbRep-Device == | ||
Das DbRep-Device wird entsprechend dieser '''Raw-Definition''' angelegt: | Das DbRep-Device wird entsprechend dieser '''Raw-Definition''' angelegt: | ||
Zeile 71: | Zeile 71: | ||
</pre> | </pre> | ||
= Auswertungsroutine "switchontime" = | == 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. | 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. | ||
Zeile 77: | Zeile 77: | ||
Den Code in die Datei kopieren: | Den Code in die Datei kopieren: | ||
< | <syntaxhighlight lang="perl"> | ||
############################################################################ | ############################################################################ | ||
# $Id: myUtilsTemplate.pm 7570 2015-01-14 18:31:44Z rudolfkoenig $ | # $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; | package main; | ||
Zeile 86: | Zeile 86: | ||
use strict; | use strict; | ||
use warnings; | use warnings; | ||
use Time::Local; | use Time::Local; | ||
Zeile 96: | Zeile 95: | ||
############################################################################################################ | ############################################################################################################ | ||
## Ermitteln der Einschaltzeiten eines Gerätes | ## Ermitteln der Einschaltzeiten eines Gerätes | ||
############################################################################################################ | ############################################################################################################ | ||
# Im DbRep-Device ist das Attribut "userExitFn = switchontime" zu setzen. | # Im DbRep-Device ist das Attribut "userExitFn = switchontime" zu setzen. | ||
Zeile 106: | Zeile 105: | ||
# switch_on_time -> on-Zeit als formatierte Angabe hhh:mm:ss | # switch_on_time -> on-Zeit als formatierte Angabe hhh:mm:ss | ||
# switch_on_time_sec -> on-Zeit als Sekundenwert | # switch_on_time_sec -> on-Zeit als Sekundenwert | ||
# | # | ||
# Die Routine ist für alle Readings geeignet die Zahlenwerte als Switch-on/off Zustand beinhalten. | # Die Routine ist für alle Readings geeignet die Zahlenwerte als Switch-on/off Zustand beinhalten. | ||
# Switch-off = 0 | # Switch-off = 0 | ||
Zeile 114: | Zeile 113: | ||
# aufgerufen. Es wird der Devicename des aufrufenden Device, Reading und Wert des Readings übergeben. | # aufgerufen. Es wird der Devicename des aufrufenden Device, Reading und Wert des Readings übergeben. | ||
# Der Aufrufablauf ist: | # Der Aufrufablauf ist: | ||
# 1. Start: state = running | # 1. Start: state = running | ||
# 2. Übermittlung jedes generierten Readings | # 2. Übermittlung jedes generierten Readings | ||
# 3. Stop: state = done (bzw. nicht running) | # 3. Stop: state = done (bzw. nicht running) | ||
# | # | ||
########################################################################################################### | ########################################################################################################### | ||
my | my $t = 0; # Variable für resultierende Sekunden | ||
my $ | my $sekunden = 0; # Variable für sekunden des Readings | ||
my $pattern = " | 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 !) | # 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"; | |||
} | |||
} | |||
my $ut = $hash->{".updateTime"}; | |||
$hash->{".updateTime"} = 0; | |||
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)"; | |||
$hash->{".updateTime"} = $ut; | |||
} | |||
return; | return; | ||
} | } | ||
1; | 1; | ||
</ | </syntaxhighlight> | ||
Aktuelle Version vom 9. November 2023, 11:56 Uhr
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 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";
}
}
my $ut = $hash->{".updateTime"};
$hash->{".updateTime"} = 0;
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)";
$hash->{".updateTime"} = $ut;
}
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.