Eigene Routinen erstellen und auslagern

FHEM bietet bereits eine Vielzahl an Funktionen und Modulen mit denen man eine Menge anstellen kann. Sei es bedinnungsabhängiges Schalten mit dem DOIF-, at- oder dem Notify-Modul. Die Möglichkeiten sind groß. Dennoch kommt es oft vor, dass man die Module durch etwas eigenen Code anpassen muss um die gewünschte Funktion zu erhalten. Oft geht dies direkt über den DEF-Editor des Moduls.

Im obigen Bild sieht man zum Beispiel die Definition eines at-Devices mit zusätzlichem eigenen Code. Hier werden zwei Variablen erstellt und mit Inhalt gefüllt um damit später zwei Dummys zu beschreiben.

In diesem Beispiel reicht es noch aus den Zusatzcode bzw. die Zusatzroutine direkt im DEF-Editor einzutragen. Möchte man jedoch mehr Code bzw. eine komplexe Routine haben, kann es sinnvoll sein, diesen auszulagern und im at-Device selber nur die erstellte Subroutine aufzurufen. Auch bei Codeabschnitten, welche man in mehreren Devices verwenden möchte ist es sinnvoll diesen auszulagern um so Änderungen am Code einfacher zu berwerkstelligen.

In diesem Beitrag möchte ich euch deswegen zeigen wie ihr euren eigen Code erstellt und diesen durch einen Aufruf der Routine verwenden könnt. 

Ort für eigene Funktionen

Für die eigenen Routinen stellt FHEM bereits einen Ort parat. Sie werden in einer Datei mit der Endung ".pm" abgespeichert. Dies ist die gleiche Endung wie für die Module in FHEM verwendet wird. Ein Modul ist also im Grunde nichts anderes als ausgelagerte Routinen. Natürlich mit ein paar zusätzliche Funktionen zur Ausgabe, Erstellung der Web-Oberfläche usw..

Diese Datei findet man in FHEM unter "Edit Files" am unteren Ende der linken Leiste innerhalb von der FHEM Web-Oberfläche.

Hier erkennt bereits, dass ich mir zwei Dateien für meine ausgelagerten Routinen erstellt habe. Zum Einen die Datei "99_myUtils.pm" für kleine Funktionen und "99_SpritalarmUtils.pm" für meinen Spritpreismonitor.

Eigene Utils-Datei anlegen

FHEM stellt bereits ein Template zur Verfügung über das man seine eigene Datei anlegen kann. Dazu das Template öffnen und über "Save as" und dem nebenstehendem Eingabefeld kann man nun seine eigene Datei erstellen. Der Name muss dabei ein paar Regeln befolgen.

Beginnen muss dieser mit "99_". Der Grund ist, dass FHEM beim Start alle Dateien lädt, welche mit diesem Präfix beginnen. Die Modul-Dateien, wie zum Beispiel die "10_FS20.pm", werden erst geladen, wenn das entsprechende Device definiert wird. In diesem Fall also ein FS20-Device. Da die eigene erstellte Datei kein eigenständiges Device definiert, sollte sie bereits beim Start von FHEM geladen werden. 

Des Weiteren sollte eure Datei mit "Utils.pm" enden. Dies bewirkt, dass die Datei unter "Edit files" gelistet wird und somit über die Web-Oberfläche editiert werden kann. 

Bevor ihr nun die Datei speichert müsst ihr die Initialisierungsroutine anpassen. Diese muss dem Namen der Datei entsprechen. Habt ihr also eure Datei "99_myFunctionsUtils.pm" genannt, so muss die Funktion "myFunctionsUtils_initialize($$)" heißen.

Nun könnt ihr die Datei zunächst erstmal speichern. Sie wird nun unter "Edit files" gelistet.

Erstellen eigener Routinen

Nachdem die Datei angelegt wurde, kann man beginnen seine Funktionen in dieser auszulagern. Ich greife als Beispiel das am Anfang dieses Beitrages erwähnte at-Device auf.

Zunächst erstellen wir uns in der angelegen "99_myFunctionsUtils.pm" eine entsprechende Routine. Wichtig ist, dass die Routine unterhalb der entsprechende Kennzeichnung "Enter your functions below _this_ line" eingefügt wird und die gesamte Datei mit "1;" abgeschlossen wird.

In diesem Fall habe ich mir nun eine Routine mit den Namen "DummysBeschreiben" erstellt. Der Name kann natürlich frei gewählt werden. Eingeleitet werden Routinen mit einem "sub". 

Wie ihr erkennt, habe ich nach dem Namen noch die beiden Klammern "()" stehen. Diese Klammern sind für die sogenannten Übergabewerte da. Da die nun erstellte Funktion keine Werte mitgegeben werden, bleiben die Klammern zunächst leer. Mehr dazu weiter unten.

Nachdem die Grundstruktur der Routine erstellt wurde kann damit begonnen werden diese mit Leben zu befüllen. Dazu einfach den Code, welche im at-Device steht in die Routine innerhalb der geschweiften Klammern einfügen.

INFO: FHEM-Routinen werden in der Programmiersprache "Perl" geschrieben. Wer noch nicht ganz fit in Perl ist, dem empfehle ich die Tutorials der Universität Linz. Die Datei kann nun mit "Save 99_myFunctionsUtils.pm" gespeichert und geschlossen werden. 

Routine aufrufen und verwenden

Routine können durch einen einfachen Routineaufruf FHEM übergreifend verwendet werden. Sei es zum Beispiel in at- notifys oder DOIF-Devices. Das Aufrufen erfolgt dabei über den Name der Routine:

DummyBeschreiben()

Anstelle den Code direkt im at-Device einzugeben, kann man dort nun die erstellte Routine aufrufen.

Routine mit Übergabewert

Manchmal ist es nötig einer Routine einen Wert mit zu geben mit der die Routine dann arbeiten kann. Wie oben schon erwähnt geschieht dies über die Klammern nach dem Routinenamen. Für jeden zu übergebenen Wert setzt man ein $-Zeichen in die Klammern.

DummyBeschreiben()

kein Übergabewert

DummyBeschreiben($)

ein Übergabewert

DummyBeschreiben($$)

zwei Übergabewerte

DummyBeschreiben($$$)

drei Übergabewerte

...

...

Um mein Beispiel treu zu bleiben, kann man nun mit Hilfe von Übergabewerten die Routine "DummysBeschreiben" etwas allgemeiner halten und somit vielseitiger einsetzbar machen. Dazu ändern wie die Routine etwas ab.

sub DummysBeschreiben ($$$) {
my ($vonDevice, $vonReading, $zuDevice) = @_;
my $a = (ReadingsVal("$vonDevice","$vonReading",0));
fhem("set $zuDevice $a");
}

Es werden der Routine demnach drei Werte übergeben. Info: Einer Variable muss immer ein $-Zeichen vorangestellt werden.In der zweiten Reihe werden die übergebenen Werte Variablen zugeordnet. Der erste übergegebene Wert wird in die Variable "$vonDevice" zugeordnet usw. Der weitere Code ähnelt dem vorherigen. Der Unterschied liegt nun daran, dass die Funktion vielseitiger eingesetzt werden kann.

Der Aufruf dieser Routine ist nun etwas anders. Man muss nun direkt beim Aufrufen die gewünschten Übergabewerte mit angeben.

DummysBeschreiben("KU_Kuehlschrank","power","KU_Kuehlschrank_power")

Man übergibt also den Namen des Devices aus dem gelesen werden soll, das zu lesende Reading und das Dummy, welches beschrieben werden soll. Das at-Device kann nun also wie folgt aufgebaut werden:

Man hat nun eine Routine erstellt mit der ein Dummy mit einem beliebigen Reading beschrieben werden kann. Es muss nur das zu lesende Device und Reading mit dem zusetzenden Dummy übergeben werden. Diese Routine kann nur je nach gebrauch benutzt werden.

Routinen mit Rückgabewert

Neben den Übergabewerte gibt es natürlich noch die Rückgabewerte. Die Routine gibt nun also Werte zurück mit denen dann weiter gearbeitet werden kann. Anders als bei den Übergabewerten kann nur ein Rückgabewert definiert werden. Möchte man mehr Rückgabewerte haben muss man den Überweg über Dummys gehen. Dazu einfach Dummys mit den Rückgaben füllen und später dann den gewünschten Wert abfragen. 

Rückgabewerte werden oft benutzt um zu überprüfen, ob die aufgerufene Routine ordnungsgemäß ausgeführt wurde bzw. werden Fehlercodes als Rückgabewert übergeben. 

sub DummysBeschreiben ($$$) {
my ($vonDevice, $vonReading, $zuDevice) = @_;
my $a = (ReadingsVal("$vonDevice","$vonReading",9999));
fhem("set $zuDevice $a");

return $a;
}

Neu hinzugekommen ist das "return $a". Es wird also die Variable "$a" als Rückgabewert zurückgegeben. Hat die Variable "$a" den Inhalt "9999" konnte das Auslesen des Devices nicht durchgeführt werden. Diese "9999" kann man nun beim Aufrufen der Routine abfragen. Hier Beispielsweise beim ersten Aufrufen der Routine.

War das Auslesen innerhalb der Routine erfolgreich, wird die Variable $a mit dem Auslesewert zurückgegeben. Im Fehlerfall wäre dieser Wert "9999". Fragt man nun also ab, ob der Wert nicht "9999" entspricht, kann man überprüfen ob erfolgreich ausgelesen wurde und zum Beispiel ein Telegramm-Nachricht verschicken.

Natürlich kann man es auch andersrum machen und explizit nach der "9999" abfragen und dann nur im Fehlerfall eine Nachricht versenden.

...

if($a == 9999) {...}

...

Interner Editor mit Syntaxhervorhebung

​Der interne Code-Editor in FHEM ist relativ schlicht gehalten und ähnelt eher einem einfach Texteditor. Mit einem zusätzlichen Attribut kann man den Editor jedoch um einiges aufwerten und ihn zu einem richtigen Code-Editor mit Syntaxhervorhebung, automatischer Vervollständigung und markieren zusammengehörender Klammern. 

Folgendes Bild zeigt den Editor wie er im "Auslieferungszustand" aussieht.

Mit folgendem Attribut kann man den Editor nun zu einem richtigen Code-Editor aufwerten:

attr TYPE=FHEMWEB JavaScripts codemirror/fhem_codemirror.js

Es wird damit ein JavaScript aktiviert, welches für die zusätzlichen Funktionen zuständig ist.

Das Attribut lässt sich auch über die FHEM-Weboberfläche einstellen. Dazu einfach das entsprechende Device öffnen (WEB, WEBphone, WEBtablet) und das Attribut "JavaScripts" setzen. Durch Löschen des Attributs, lässt sich der alte Editor wiederherstellen. Wenn ihr mir zum Editor und die erlaubten Tastenkombinationen erfahren wollt, schaut am besten im FHEM-Wiki nach.

Hinterlasse einen Kommentar

2 Kommentare auf "Eigene Routinen erstellen und auslagern"

Benachrichtige mich zu:
avatar
Fotos und Bilder
 
 
 
sonstiges Dateiformat
 
 
 
Sortiert nach:   neuste | älteste | beste Bewertung
Jan
Gast

Hi,

super Einführung in die „FHEM-Programmierung“, auch verständlich für Nicht-Programmierer.
Besonderer Dank für den Tipp mit dem Syntax-Highlighting! Kannte ich vorher noch nicht und habe mich jedes Mal wieder beim Editieren von Code geärgert, dass es hier keinerlei Highlighting o.ä. gibt.

Zwei Hinweise, die du evtl. noch hinzufügen kannst:
1. Das zu setzende Attribut befindet sich in FHEMWEB (normal/Tablet/Phone). Kann somit auch über die UI gesetzt werden.
2. FHEM-Routinen sind reines Perl. Ein Link auf ein Perl-Tutorial könnte man hier noch hinzufügen. Sonst rätselt man vielleicht, wenn es dann um if/else oder weitere Anweisungen geht.

Gruß,
Jan

wpDiscuz