FHEM ist sehr komplex und häufig wiederholen sich die teilweise sehr langen Befehlsketten. Aus diesem Grund habe ich verschiedene Funktionen geschrieben, dir mir die Programmierung erleichtern (dkUtils.pm). So ist mein Helferlein dkUtils entstanden.

Im Folgenden möchte ich euch diese Funktionen vorstellen. Wenn ihr die einzelnen Code-Fragmente in eine Datei kopiert, habt ihr meine dkUtils.

Über konstruktive Verbesserungen in den Kommentaren bin ich dankbar.

dkUtils.pm: Genrelles


##############################################
## Stand: 13.02.2016
##############################################
# $Id$
package main;

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


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

So wird jedes eigene Modul (dkUtils) initialisiert und einige Abhängigkeiten wie z. B: zu Time::Local hergestellt.

dkUtils.pm: Allgemeine Funktionen


#######################################
##
##
## ALLGEMEINE FUNKTIONEN
##
##
#######################################

sub dkOn($) {
	my($device) = @_;
	fhem("set $device on");
}


sub dkOff($) {
	my($device) = @_;
	fhem("set $device off");
}


sub dkToggle($) {
	my ($device) = @_;
	fhem("set $device toggle");
}

Diese Funktionen schalten Geräte ein oder aus, bzw. ändern deren Zustand abhängig davon ob sie gerade ein- oder ausgeschaltet sind.


sub dkAutoOff($$$) {
	my ($device, $event, $durationInHours) = @_;
	my ($deviceAutoOff) = $device . "_AutoOff";
	if (dkExists($deviceAutoOff)) { fhem ("delete $deviceAutoOff"); }
	if ($event eq "on") {
		my $mytime = dkTimeAddHoursHMS($durationInHours);
		fhem ("define $deviceAutoOff at $mytime { fhem('set $device off') }");
	}
}

Die Funktion dkAutoOff sorgt dafür, dass ein Gerät nach einer gewissen Zeit in Stunden (0.5 für eine halbe Stunde geht auch) ausgeschaltet wird. Dies könnte man auch über „on-till“ oder „on-for-timer“ jedoch lösen diese Timer im Falle eines Neustarts nicht aus.
Achtung: Diese Funktion hat weitere Abhängigkeiten. Zum einen dkExists() (prüft ob Gerät vorhanden) und dkTimeAddHoursHMS() (addiert zur aktuellen Zeit x Stunden).
Eine ausführliche Beschreibung findet ihr in dem Beitrag AutoOff für Geräte erstellen.

dkUtils.pm: Zustände, Gerätenamen und Parameter


#######################################
##
##
## Device States, Alias und Parameter
##
##
#######################################

sub dkGetReading($$){
	my($device, $reading) = @_;
	return ReadingsVal($device, $reading, "unknown");
}


sub dkGetState($){
	my($device) = @_;
	return ReadingsVal($device, "state", "unknown");
}


sub dkGetStateLastChange($) {
	my($device) = @_;
	return ReadingsTimestamp($device,"state","0");
}


sub dkGetAttr($$) {
	my($device, $attr) = @_;
	return AttrVal($device, $attr, "unknown");
}


sub dkGetAlias($) {
	my($device) = @_;
	return AttrVal($device, "alias", "unknown");
}


sub dkSetValue($$) {
	my($device, $value) = @_;
	fhem("set $device $value");
}


sub dkSetReading($$$) {
	my($device, $reading, $value) = @_;
	fhem("setreading $device $reading $value");
}

#######################################

sub dkExists($) {
	my ($object) = @_;
	if ( Value($object) ) {	return 1; } else { return 0; }
}


sub dkValueExists($) {
	my ($object) = @_;
	if ( Value($object) eq "???" || ReadingsVal($object, "state", "???") eq "???" ) {
		return 0;
	} else {
		return 1;
	}
}

Mit den obigen Funktionen kann ich relativ einfach einen Gerätenamen (Alias) sowie Werte und Zustände von Geräten abfragen und setzen. Darüber hinaus kann überprüft werden, ob ein Gerät bzw. ein Value existiert.

dkUtils.pm: Watchdog


#######################################
##
##
## Watchdog
##
##
#######################################

sub dkWatchdog($$$) {
	my ($device, $state, $durationInHours) = @_;
	my $device_state = dkGetState($device);
	my $device_alias = dkGetAlias($device);

	dkRemoveWatchdog($device);
	if ($device_state eq $state) {
		dkAddWatchdog($device,$state,$durationInHours);
		dkPush("device", "$device_alias ist noch angeschaltet.");
	}
}


sub dkAddWatchdog($$$) {
	my($device, $state, $durationInHours) = @_;
	my $device_notify = $device . "_dkWatchdog";
	my $mytime = dkTimeAddHoursHMS($durationInHours);
	fhem("define $device_notify at $mytime { dkWatchdog('$device', '$state', $durationInHours) }");
}


sub dkRemoveWatchdog($) {
	my($device) = @_;
	my $device_notify = $device . "_dkWatchdog";
	if (dkExists($device_notify)) {
		fhem ("delete $device_notify");
	}
}

Mein dkWatchdog sorgt dafür, dass nach einer bestimmten Zeit überprüft wird, ob ein Gerät noch angeschaltet ist. Schalte ich z. B. eine Pumpe an, so wird der Watchdog gesetzt (dkAddWatchdog), schalte ich sie aus, wird der Watchdog wieder gelöscht (dkRemoveWatchdog). Nach dem Anschalten wird z. B. nach einer Stunde überprüft (dkWatchdog), ob das Gerät noch an ist und entsprechend eine Push-Notification verschickt. So kann sichergestellt werden, dass man nie vergisst Geräte wieder auszuschalten.

dkUtils.pm: Zeit-Funktionen


#######################################
##
##
## Zeit-Funktionen
##
##
#######################################

sub dkCurrentHour($$) {
	my ($operator,$hour) = @_;
	my $currenthour = strftime("%H", localtime)+0; # +0 = hack to format as integer
	my $command = "$currenthour $operator $hour";
	if (eval($command)) {
		return 1;
	} else {
		return 0;
	}
}


sub dkTimeFormat($){
	my $Sekundenzahl = @_;
	return sprintf("%02d:%02d:%02d", ($Sekundenzahl/60/60)%24, ($Sekundenzahl/60)%60, ($Sekundenzahl)%60 );
}


sub dkTimeAgeInHours($$) {
	my ($now, $timestamp) = @_;
	my @splitdatetime = split(/ /,$timestamp);
	my @splitdate = split(/-/, $splitdatetime[0]);
	my @splittime = split(/:/, $splitdatetime[1]);
	my $last_state_time =  timelocal($splittime[2], $splittime[1], $splittime[0], $splitdate[2], $splitdate[1]-1, $splitdate[0]);
	my $age_in_hours = ($now - $last_state_time) / 3600;
	return $age_in_hours;
}

sub dkTimeHoursToHMS($) {
	my ($hours) = @_;
	$hours = $hours * 60 * 60;
	return strftime("\%H:\%M:\%S", gmtime($hours) );
}

sub dkTimeAddHoursHMS($) {
	my ($hours) = @_;
	$hours = $hours * 60 * 60;
	return strftime("\%H:\%M:\%S", localtime(time + $hours) );
}

Diese Funktionen konvertieren oder berechnen Timestamps oder HMS-Zeitangaben (also Stunde:Minute:Sekunde).
Die Funktion dkTimeAgeInHours kann z. B. dafür verwendet werden, um herauszufinden, ob sich ein Wert innerhalb einer bestimmten Zeit verändert hat.

dkUtils.pm: Sprachausgabe


#######################################
##
##
## Sprachausgabe
##
##
#######################################

sub dkTalk($) {
	my ($message) = @_;
	if (!$message) { return; }
	$message =~ s/_/ /g;
	$message =~ s/-/ /g;
	fhem("set mytts tts $message");
}


dkTalk ermöglicht die Sprachausgabe. Bevor die Nachricht an das Gerät übermittelt wird, werden "_" und "-" entfernt, damit eine unterbrechungsfreie Ausgabe erfolgt und diese Zeichen nicht gesprochen werden.


<h2>dkUtils.pm: Push-Nachrichten</h2>


#######################################
##
##
## Push Mitteilungen
##
##
#######################################

sub dkPush($$) {
	my ($type, $message) = @_;
	if (!$message) { return; }
	if ($type eq "default") 	{ fhem("set pushover msg '$message'"); }
	if ($type eq "warning") 	{ fhem("set pushover msg 'FHEM Warning' '$message' '' 0 'gamelan' "); }
	if ($type eq "call") 		{ fhem("set pushover msg 'FHEM' '$message' '' 0 'bike' "); }
	if ($type eq "device") 		{ fhem("set pushover msg 'FHEM' '$message' '' 0 'bike' "); }
	if ($type eq "attention") 	{ fhem("set pushover msg 'FHEM' '$message' '' 0 'bugle' "); }
	if ($type eq "magic") 		{ fhem("set pushover msg 'FHEM' '$message' '' 0 'magic' "); }
}

Mit dkPush können Push-Nachrichten angestoßen werden. Je nach gesetzten Type ertönt in der App ein anderer Signalton.

dkUtils.pm: Textausgabe auf Fernseher


#######################################
##
##
## Textausgabe auf Fernseher
##
##
#######################################

sub dkDisplayText($) {
	my ($message) = @_;
	if (!$message) { return; }
	if ( ReadingsVal("SatReceiver", "presence", "unknown") eq "present") {
		fhem("set SatReceiver msg info 10 $message");
	}
}

Diese Funktion sorgt dafür, dass auf meinem Enigma2-Sat-Receiver ein Mitteilung erscheint (z. B. wenn eine Anruf eingeht).

dkUtils.pm: FHEM-Start/Stop


#######################################
##
##
## FHEM ROUTINES
##
##
#######################################

sub dkFHEM($) {
	my ($event) = @_;

	if ($event eq "INITIALIZED") {
		dkSetDefaults();
		fhem("set SCC led 00");
		dkTalk("System gestartet.");
	}

	if ($event eq "SHUTDOWN") {
		dkTalk("System fährt runter.");
	}

}

Je nach Status von FHEM (Initialized oder Shutdown) erfolgt eine kurze Sprachausgabe.

dkUtils.pm: Helpers


#######################################
##
##
## HELPERS
##
##
#######################################

sub _isWeekend() {
	my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = localtime;
	# Samstag = 0, Sonntag = 6
	if($wday == 0 || $wday == 6) {
		return 1;
	} else {
		return 0;
	}
}

#######################################

1;

Diese interne Funktion gibt zurück, ob es Wochenende ist oder nicht.

Bitte nicht vergessen am Ende der Datei ein "1;" einzufügen (siehe letzte Zeile Helpers)

Fazit

Sicherlich könnten die Funktionen in dkUtils an der ein oder anderen Stelle noch optimiert werden, aber sie helfen mir Schreibarbeit zu reduzieren und sorgen dafür, dass der eigene Code lesbarer wird und aufgeräumter wird.