Summe aller Einschaltzeiten eines Gerätes

Aus FHEMWiki
Wechseln zu: Navigation, Suche

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 $
# 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.