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:

<source lang="perl">

  1. $Id: myUtilsTemplate.pm 7570 2015-01-14 18:31:44Z rudolfkoenig $
  2. 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) = @_;

}

    1. Ermitteln der Einschaltzeiten eines Gerätes
  1. Im DbRep-Device ist das Attribut "userExitFn = switchontime" zu setzen.
  2. Auswertung wird im DbRep gestartet mit:
  3. set <DbRep-Device> fetchrows history
  4. Die resultierende Switch-on Zeit wird im aufrufenden DbRep-Device in Readings dargestellt:
  5. switch_on_time -> on-Zeit als formatierte Angabe hhh:mm:ss
  6. switch_on_time_sec -> on-Zeit als Sekundenwert
  7. Die Routine ist für alle Readings geeignet die Zahlenwerte als Switch-on/off Zustand beinhalten.
  8. Switch-off = 0
  9. Swich-off  != 0
  10. Nach der Erstellung jedes einzelnen Readings wird die im Attribut "userExitFn" hinterlegte Funktion
  11. aufgerufen. Es wird der Devicename des aufrufenden Device, Reading und Wert des Readings übergeben.
  12. Der Aufrufablauf ist:
  13. 1. Start: state = running
  14. 2. Übermittlung jedes generierten Readings
  15. 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.