DOIF/Perl-Modus: Unterschied zwischen den Versionen

Aus FHEMWiki
(Übernahme aus der Commandref)
 
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
Perl Modus
Der Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Der Anwender hat mehr Einfluss auf den Ablauf der Steuerung als im FHEM-Modus. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Mit Hilfe von Templates können generalisiert DOIFs definiert werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt, zum Zeitpunkt der Ausführung wird nur noch Perl ausgeführt, dadurch wird maximale Performance gewährleistet.
Der Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Der Anwender hat mehr Einfluss auf den Ablauf der Steuerung als im FHEM-Modus. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Mit Hilfe von Templates können generalisiert DOIFs definiert werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt, zum Zeitpunkt der Ausführung wird nur noch Perl ausgeführt, dadurch wird maximale Performance gewährleistet.


Syntax Perl-Modus:
Syntax Perl-Modus:


<syntaxhighlight lang="perl">
{
define <name> DOIF <Template-Definitionen (optional)> <DOIF-Blöcke>
define <name> DOIF <Template-Definitionen (optional)> <DOIF-Blöcke>
}


Syntax Template-Definition:
Syntax Template-Definition:


<syntaxhighlight lang="perl">
{
DEF TPL_<Template-Name>(<DOIF-Block-Definition mit Platzhaltern: $1,$2,...>)
DEF TPL_<Template-Name>(<DOIF-Block-Definition mit Platzhaltern: $1,$2,...>)
 
}
Anwendungsbeispiel zu DOIF-Templates


Syntax DOIF-Block:
Syntax DOIF-Block:


<syntaxhighlight lang="perl">
{
<Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>}
<Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>}
}


Ein DOIF-Block wird ausgeführt, wenn dieser bedingt durch Ereignis- und Zeittrigger in eckigen Klammern innerhalb des Blocks, getriggert wird. Es wird die vollständige Perl-Syntax unterstützt. Es können beliebig viele Blöcke innerhalb eines DOIF-Devices definiert werden. Sie werden unabhängig voneinander durch passende Trigger ausgeführt. Der Name eines Blocks ist optional.
Ein DOIF-Block wird ausgeführt, wenn dieser bedingt durch Ereignis- und Zeittrigger in eckigen Klammern innerhalb des Blocks, getriggert wird. Es wird die vollständige Perl-Syntax unterstützt. Es können beliebig viele Blöcke innerhalb eines DOIF-Devices definiert werden. Sie werden unabhängig voneinander durch passende Trigger ausgeführt. Der Name eines Blocks ist optional.
Zeile 34: Zeile 39:


Einfache Anwendungsbeispiele (vgl. Anwendungsbeispiele im FHEM-Modus):
Einfache Anwendungsbeispiele (vgl. Anwendungsbeispiele im FHEM-Modus):
 
<syntaxhighlight lang="perl">
{
define di_rc_tv DOIF {if ([remotecontol:"on"]) {fhem_set"tv on"} else {fhem_set"tv off"}}
define di_rc_tv DOIF {if ([remotecontol:"on"]) {fhem_set"tv on"} else {fhem_set"tv off"}}


Zeile 40: Zeile 46:


define di_lamp DOIF {if ([06:00-09:00] and [sensor:brightness] < 40) {fhem_set"lamp:FILTER=STATE!=on on"} else {fhem_set"lamp:FILTER=STATE!=off off"}}
define di_lamp DOIF {if ([06:00-09:00] and [sensor:brightness] < 40) {fhem_set"lamp:FILTER=STATE!=on on"} else {fhem_set"lamp:FILTER=STATE!=off off"}}
}


Bemerkung: Im Gegensatz zum FHEM-Modus arbeitet der Perl-Modus ohne Auswertung des eigenen Status (Zustandsauswertung), daher muss der Anwender selbst darauf achten, wiederholende Ausführungen zu vermeiden (im oberen Beispiel z.B. mit FILTER-Option). Elegant lässt sich das Problem der wiederholenden Ausführung bei zyklisch sendenden Sensoren mit Hilfe des Attributes DOIF_Readings lösen.
Bemerkung: Im Gegensatz zum FHEM-Modus arbeitet der Perl-Modus ohne Auswertung des eigenen Status (Zustandsauswertung), daher muss der Anwender selbst darauf achten, wiederholende Ausführungen zu vermeiden (im oberen Beispiel z.B. mit FILTER-Option). Elegant lässt sich das Problem der wiederholenden Ausführung bei zyklisch sendenden Sensoren mit Hilfe des Attributes DOIF_Readings lösen.
Zeile 77: Zeile 84:
Bemerkung: Innerhalb eines Ereignisblocks muss mindestens ein Trigger definiert werden, damit der gesamte Block beim passenden Trigger ausgeführt wird.
Bemerkung: Innerhalb eines Ereignisblocks muss mindestens ein Trigger definiert werden, damit der gesamte Block beim passenden Trigger ausgeführt wird.


Inhaltsübersicht Perl-Modus
==Eigene Funktionen==
 
Eigene Funktionen - subs-Block
Eigene Funktionen mit Parametern
Eigener_Namensraum
Spezifische Perl-Funktionen im Perl-Modus:  fhem_set  set_Event  set_State  get_State  set_Reading  get_Reading  set_Reading_Update
Ausführungstimer:  set_Exec  get_Exec  del_Exec
Initialisierung - init-Block
Device-Variablen
Blockierende Funktionsaufrufe
Attribute im Perl-Modus
set-Befehle im Perl-Modus
Anwendungsbeispiele im Perl-Modus
 
Eigene Funktionen  back


Ein besonderer Block ist der Block namens subs. In diesem Block werden Perlfunktionen definiert, die innerhalb des DOIFs genutzt werden. Um eine möglichst hohe Kompatibilität zu Perl sicherzustellen, wird keine DOIF-Syntax in eckigen Klammern unterstützt, insb. gibt es keine Trigger, die den Block ausführen können.
Ein besonderer Block ist der Block namens subs. In diesem Block werden Perlfunktionen definiert, die innerhalb des DOIFs genutzt werden. Um eine möglichst hohe Kompatibilität zu Perl sicherzustellen, wird keine DOIF-Syntax in eckigen Klammern unterstützt, insb. gibt es keine Trigger, die den Block ausführen können.
Zeile 97: Zeile 90:
Beispiel:
Beispiel:


<syntaxhighlight lang="perl">
{
DOIF subs { ## Definition von Perlfunktionen lamp_on und lamp_off
DOIF subs { ## Definition von Perlfunktionen lamp_on und lamp_off
   sub lamp_on {
   sub lamp_on {
Zeile 109: Zeile 104:
{[06:00];lamp_on()}  ## Um 06:00 Uhr wird die Funktion lamp_on aufgerufen
{[06:00];lamp_on()}  ## Um 06:00 Uhr wird die Funktion lamp_on aufgerufen
{[08:00];lamp_off()} ## Um 08:00 Uhr wird die Funktion lamp_off aufgerufen
{[08:00];lamp_off()} ## Um 08:00 Uhr wird die Funktion lamp_off aufgerufen
}


Eigene Funktionen mit Parametern


Eigene Funktionen mit Parametern  back
Unter Verwendung von Funktionsparameter lassen sich Definitionen oft vereinfachen, das obige Beispiel lässt sich mit Hilfe nur einer Funktion kürzer wie folgt definieren:
 
Unter Verwendung von Funktionsparamerter lassen sich Definitionen oft vereinfachen, das obige Beispiel lässt sich mit Hilfe nur einer Funktion kürzer wie folgt definieren:


<syntaxhighlight lang="perl">
{
DOIF subs { ## Definition der Perlfunktion lamp
DOIF subs { ## Definition der Perlfunktion lamp
   sub lamp {
   sub lamp {
Zeile 124: Zeile 121:
{[06:00];lamp("on")}  ## Um 06:00 Uhr wird die Funktion lamp mit Parameter "on" aufgerufen
{[06:00];lamp("on")}  ## Um 06:00 Uhr wird die Funktion lamp mit Parameter "on" aufgerufen
{[08:00];lamp("off")} ## Um 08:00 Uhr wird die Funktion lamp mit dem Parameter "off" aufgerufen
{[08:00];lamp("off")} ## Um 08:00 Uhr wird die Funktion lamp mit dem Parameter "off" aufgerufen
}


 
==Eigener Namensraum: DOIF==
Eigener Namensraum   back


Der Namensraum im Perl-Modus ist gekapselt. Selbstdefinierte Funktionen im DOIF-Device können nicht bereits existierende Perlfunktionen in FHEM (Namensraum main) überschreiben. Funktionen aus dem Namensraum main müssen mit vorangestellem Doppelpunkt angegeben werden: ::<perlfunction>
Der Namensraum im Perl-Modus ist gekapselt. Selbstdefinierte Funktionen im DOIF-Device können nicht bereits existierende Perlfunktionen in FHEM (Namensraum main) überschreiben. Funktionen aus dem Namensraum main müssen mit vorangestellem Doppelpunkt angegeben werden: ::<perlfunction>


Eigene Perlfunktionen, die in myutils ausgelagert sind, befinden sich ebenfalls im Namensraum main. Wenn sie ausschließich in DOIF-Devices benutzt werden sollen, so kann am Anfang vor deren Definition in myutils "package DOIF;" angegeben werden. In diesen Fall sind auch diese Funktion im DOIF-Device bekannt - sie können dann ohne vorangestellten Doppelpunkt genutzt werden.
Eigene Perlfunktionen, die in myutils ausgelagert sind, befinden sich ebenfalls im Namensraum main. Wenn sie ausschließlich in DOIF-Devices benutzt werden sollen, so kann am Anfang vor deren Definition in myutils "package DOIF;" angegeben werden. In diesen Fall sind auch diese Funktion im DOIF-Device bekannt - sie können dann ohne vorangestellten Doppelpunkt genutzt werden.


Folgende FHEM-Perlfunktionen wurden ebenfalls im DOIF-Namensraum definiert, sie können, wie gewohnt ohne Doppelpunkt genutzt werden:
Folgende FHEM-Perlfunktionen wurden ebenfalls im DOIF-Namensraum definiert, sie können, wie gewohnt ohne Doppelpunkt genutzt werden:
Zeile 136: Zeile 133:
fhem, Log, Log3, InternVal, InternalNum, OldReadingsVal, OldReadingsNum, OldReadingsTimestamp, ReadingsVal, ReadingsNum, ReadingsTimestamp, ReadingsAge, Value, OldValue, OldTimestamp, AttrVal, AttrNum
fhem, Log, Log3, InternVal, InternalNum, OldReadingsVal, OldReadingsNum, OldReadingsTimestamp, ReadingsVal, ReadingsNum, ReadingsTimestamp, ReadingsAge, Value, OldValue, OldTimestamp, AttrVal, AttrNum


Spezifische Perl-Funktionen im Perl-Modus   back
==Spezifische Perl-Funktionen im Perl-Modus==


FHEM set-Befehl ausführen: fhem_set(<content>), mit <content> Übergabeparameter des FHEM set-Befehls
FHEM set-Befehl ausführen: fhem_set(<content>), mit <content> Übergabeparameter des FHEM set-Befehls
Zeile 195: Zeile 192:
set_Reading_End(1);
set_Reading_End(1);


Ausführungstimer   back
==Ausführungstimer==


Mit Hilfe von Ausführungstimern können Perl-Anweisungen verzögert ein- oder mehrmalig ausgeführt werden. Im Gegensatz zum FHEM-Modus können beliebig viele Timer gleichzeitig genutzt werden. Ein Ausführungstimer wird mit einem Timer-Namen eindeutig definiert. Über den Timer-Namen kann die Restlaufzeit abgefragt werden, ebenfalls kann er vor seinem Ablauf gelöscht werden.
Mit Hilfe von Ausführungstimern können Perl-Anweisungen verzögert ein- oder mehrmalig ausgeführt werden. Im Gegensatz zum FHEM-Modus können beliebig viele Timer gleichzeitig genutzt werden. Ein Ausführungstimer wird mit einem Timer-Namen eindeutig definiert. Über den Timer-Namen kann die Restlaufzeit abgefragt werden, ebenfalls kann er vor seinem Ablauf gelöscht werden.
Zeile 209: Zeile 206:
Wird set_Exec mit dem gleichen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.
Wird set_Exec mit dem gleichen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.


Timer holen: get_Exec(<timerName>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers
Restzeit des Timers: get_Exec(<timerName>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers


Laufenden Timer löschen: del_Exec(<timerName>)
Laufenden Timer löschen: del_Exec(<timerName>)
Zeile 249: Zeile 246:
Wenn Fenster geöffnet wird, dann soll eine Benachrichtigung erfolgen, dabei soll die Benachrichtigung bis zu 10 mal jeweils um weitere 60 Sekunden verzögert werden: erste Benachrichtigung nach 5 Minuten, zweite Benachrichtigung nach weiteren 6 Minuten, dritte Benachrichtigung nach weiteren 7 Minuten usw.
Wenn Fenster geöffnet wird, dann soll eine Benachrichtigung erfolgen, dabei soll die Benachrichtigung bis zu 10 mal jeweils um weitere 60 Sekunden verzögert werden: erste Benachrichtigung nach 5 Minuten, zweite Benachrichtigung nach weiteren 6 Minuten, dritte Benachrichtigung nach weiteren 7 Minuten usw.


<syntaxhighlight lang="perl">
{
defmod di_window DOIF {if ([window:state] eq "open") {\
defmod di_window DOIF {if ([window:state] eq "open") {\
   set_Exec("timer",'300+$count*60','fhem("echo speak window open")','$count < 9')\
   set_Exec("timer",'300+$count*60','fhem("echo speak window open")','$count < 9')\
Zeile 254: Zeile 253:
   del_Exec("timer")\
   del_Exec("timer")\
}\
}\
}
}
}


Pumpe alle zwei Stunden im Abstand von fünf Minuten 3 mal einschalten, beim ersten mal für 60 Sekunden, beim zweiten mal für 80 Sekunden und beim dritten mal für 100 Sekunden.
Pumpe alle zwei Stunden im Abstand von fünf Minuten 3 mal einschalten, beim ersten mal für 60 Sekunden, beim zweiten mal für 80 Sekunden und beim dritten mal für 100 Sekunden.


<syntaxhighlight lang="perl">
{
defmod di_pump DOIF {[08:00-20:00,+[2]:00];;set_Exec("timer",300,'fhem_set("pump on-for-timer ".60+$count*20)','$count < 3')}
defmod di_pump DOIF {[08:00-20:00,+[2]:00];;set_Exec("timer",300,'fhem_set("pump on-for-timer ".60+$count*20)','$count < 3')}
}


Hochdimmen beim Tastendruck im Sekundentakt.
Hochdimmen beim Tastendruck im Sekundentakt.


<syntaxhighlight lang="perl">
{
defmod di_dimm DOIF {["button:on"];;@{$_pct}=(10,35,65,80,90,100);;set_Exec("timer",1,'fhem_set"lamp pct ".$_pct[$count]','$count < 6')}
defmod di_dimm DOIF {["button:on"];;@{$_pct}=(10,35,65,80,90,100);;set_Exec("timer",1,'fhem_set"lamp pct ".$_pct[$count]','$count < 6')}
}


$_pct ist hier ein Array mit Helligkeitswerten, auf das innerhalb des Devices zu jedem Zeitpunkt zugegriffen werden kann.
$_pct ist hier ein Array mit Helligkeitswerten, auf das innerhalb des Devices zu jedem Zeitpunkt zugegriffen werden kann.




init-Block   back
==init-Block==


Wird ein Ereignisblock mit dem Namen init benannt, so wird dieser Block beim Systemstart ausgeführt. Er bietet sich insb. an, um Device-Variablen des Moduls vorzubelegen.
Wird ein Ereignisblock mit dem Namen init benannt, so wird dieser Block beim Systemstart ausgeführt. Er bietet sich insb. an, um Device-Variablen des Moduls vorzubelegen.


Device-Variablen   back
==Device-Variablen==


Device-Variablen sind sogenannte Instanzvariablen, die global innerhalb eines DOIF-Devices genutzt werden können. Deren Inhalt bleibt von Trigger zu Trigger während der Laufzeit des System erhalten. Sie beginnen mit $_ und müssen nicht deklariert werden. Wenn sie nicht vorbelegt werden, gelten sie als nicht definiert. Das lässt sich abfragen mit:
Device-Variablen sind sogenannte Instanzvariablen, die global innerhalb eines DOIF-Devices genutzt werden können. Deren Inhalt bleibt von Trigger zu Trigger während der Laufzeit des System erhalten. Sie beginnen mit $_ und müssen nicht deklariert werden. Wenn sie nicht vorbelegt werden, gelten sie als nicht definiert. Das lässt sich abfragen mit:
Zeile 277: Zeile 283:
if (defined $_...) ...
if (defined $_...) ...


Instanzvariablen überleben nicht den Neustart, sie können jedoch z.B. im init-Block, der beim Systemstart ausgewertet wird, aus Readings vorbelegt werden.
Instanzvariablen überleben nicht den Neustart, sie können jedoch z.B. im init-Block, der beim Systemstart ausgewertet wird, aus mit Inhalten aus Readings initialisiert werden.


Bsp. Vorbelgung einer Instanzvariablen beim Systemstart mit dem Status des Moduls:
Bsp. Initialisierunger einer Instanzvariablen namens $_status, beim Systemstart mit dem Status des Moduls:


init {$_status=get_State()}
init {$_status=get_State()}
Zeile 291: Zeile 297:
$_betrag{heute}=100;
$_betrag{heute}=100;


Blockierende Funktionsaufrufe (blocking calls)   back
==Blockierende Funktionsaufrufe (blocking calls)==


DOIF verwaltet blockierende Funktionsaufrufe, d.h. die in diesem Zusammenhang gestarteten FHEM-Instanzen werden gelöscht, beim Herunterfahren (shutdown), Wiedereinlesen der Konfiguration (rereadcfg) Änderung der Konfiguration (modify) und Deaktivieren des Gerätes (disabled).
DOIF verwaltet blockierende Funktionsaufrufe, d.h. die in diesem Zusammenhang gestarteten FHEM-Instanzen werden gelöscht, beim Herunterfahren (shutdown), Wiedereinlesen der Konfiguration (rereadcfg) Änderung der Konfiguration (modify) und Deaktivieren des Gerätes (disabled).
Zeile 307: Zeile 313:
$_blockingcalls ist eine für DOIF reservierte Variable und darf nur in der beschriebener Weise verwendet werden.
$_blockingcalls ist eine für DOIF reservierte Variable und darf nur in der beschriebener Weise verwendet werden.


Nutzbare Attribute im Perl-Modus   back
==Nutzbare Attribute im Perl-Modus==


addStateEvent  checkReadingEvent  DOIF_Readings  disable  event_Readings  notexist  readingList  setList  uiTable  uiState  weekdays   
addStateEvent  checkReadingEvent  DOIF_Readings  disable  event_Readings  notexist  readingList  setList  uiTable  uiState  weekdays   
Zeile 313: Zeile 319:




set-Befehle im Perlmodus   back
set-Befehle im Perlmodus
set <name> disable
set <name> disable
blockiert die Befehlsausführung
blockiert die Befehlsausführung
Zeile 323: Zeile 329:
führt den entsprechenden DOIF-Block aus
führt den entsprechenden DOIF-Block aus


Anwendungsbeispiele im Perlmodus:  back
==Anwendungsbeispiele im Perlmodus==


Treppenhauslicht mit Bewegungsmelder
Treppenhauslicht mit Bewegungsmelder
 
<syntaxhighlight lang="perl">
{
defmod di_light DOIF {\
defmod di_light DOIF {\
   if (["FS:motion"]) {                          # bei Bewegung\
   if (["FS:motion"]) {                          # bei Bewegung\
Zeile 333: Zeile 340:
   }\
   }\
}
}
 
}
Einknopf-Fernbedienung
Einknopf-Fernbedienung


Anforderung: Wenn eine Taste innerhalb von zwei Sekunden zwei mal betätig wird, soll der Rollladen nach oben, bei einem Tastendruck nach unten.
Anforderung: Wenn eine Taste innerhalb von zwei Sekunden zwei mal betätig wird, soll der Rollladen nach oben, bei einem Tastendruck nach unten.


<syntaxhighlight lang="perl">
{
defmod di_shutter DOIF {\
defmod di_shutter DOIF {\
   if (["FS:^on$"] and !get_Exec("shutter")){          # wenn Taste betätigt wird und kein Timer läuft\
   if (["FS:^on$"] and !get_Exec("shutter")){          # wenn Taste betätigt wird und kein Timer läuft\
Zeile 346: Zeile 355:
   }\
   }\
}
}
 
}
Aktion auslösen, wenn innerhalb einer bestimmten Zeitspanne ein Ereignis x mal eintritt
Aktion auslösen, wenn innerhalb einer bestimmten Zeitspanne ein Ereignis x mal eintritt


Im folgenden Beispiel wird die Nutzung von Device-Variablen demonstriert.
Im folgenden Beispiel wird die Nutzung von Device-Variablen demonstriert.


<syntaxhighlight lang="perl">
{
defmod di_count DOIF {\
defmod di_count DOIF {\
   if (["FS:on"] and !get_Exec("counter")) {                                        # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft\
   if (["FS:on"] and !get_Exec("counter")) {                                        # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft\
Zeile 358: Zeile 369:
     $_count++;;                                                                    # wenn Timer bereits läuft zähle Ereignis\
     $_count++;;                                                                    # wenn Timer bereits läuft zähle Ereignis\
   }\
   }\
}
}
}

Version vom 28. Dezember 2021, 16:28 Uhr

Der Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Der Anwender hat mehr Einfluss auf den Ablauf der Steuerung als im FHEM-Modus. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Mit Hilfe von Templates können generalisiert DOIFs definiert werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt, zum Zeitpunkt der Ausführung wird nur noch Perl ausgeführt, dadurch wird maximale Performance gewährleistet.

Syntax Perl-Modus:

<syntaxhighlight lang="perl"> { define <name> DOIF <Template-Definitionen (optional)> <DOIF-Blöcke> }

Syntax Template-Definition:

<syntaxhighlight lang="perl"> { DEF TPL_<Template-Name>(<DOIF-Block-Definition mit Platzhaltern: $1,$2,...>) }

Syntax DOIF-Block:

<syntaxhighlight lang="perl"> { <Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>} }

Ein DOIF-Block wird ausgeführt, wenn dieser bedingt durch Ereignis- und Zeittrigger in eckigen Klammern innerhalb des Blocks, getriggert wird. Es wird die vollständige Perl-Syntax unterstützt. Es können beliebig viele Blöcke innerhalb eines DOIF-Devices definiert werden. Sie werden unabhängig voneinander durch passende Trigger ausgeführt. Der Name eines Blocks ist optional.

Der Status des Moduls wird nicht vom Modul gesetzt, er kann vom Anwender mit Hilfe der Funktion set_State verändert werden, siehe spezifische Perl-Funktionen im Perl-Modus. FHEM-Befehle werden durch den Aufruf der Perlfunktion fhem("...") ausgeführt. Für den häufig genutzten fhem-Befehl set wurde eine kompatible Perlfunktion namens fhem_set definiert. Sie ist performanter und sollte bevorzugt verwendet werden, da das Parsen nach dem FHEM set-Befehl entfällt.

Der Benutzer kann mit der Funktion set_Exec beliebig viele eigene Timer definieren, die unabhängig voneinander gesetzt und ausgewertet werden können, siehe Spezifische Perl-Funktionen im Perl-Modus.

Definitionen im FHEM-Modus mit do-Attribut der Form:

DOIF (<Bedingung mit Trigger>) (<FHEM-Befehle>) DOELSE (<FHEM-Befehle>)

lassen sich wie folgt in Perl-Modus übertragen:

DOIF {if (<Bedingung mit Trigger>) {fhem"<FHEM-Befehle>"} else {fhem"<FHEM-Befehle>"}}

Die Bedingungen des FHEM-Modus können ohne Änderungen in Perl-Modus übernommen werden.

Einfache Anwendungsbeispiele (vgl. Anwendungsbeispiele im FHEM-Modus): <syntaxhighlight lang="perl"> { define di_rc_tv DOIF {if ([remotecontol:"on"]) {fhem_set"tv on"} else {fhem_set"tv off"}}

define di_clock_radio DOIF {if ([06:30|Mo Di Mi] or [08:30|Do Fr Sa So]) {fhem_set"radio on"}} {if ([08:00|Mo Di Mi] or [09:30|Do Fr Sa So]) {fhem_set"radio off"}}

define di_lamp DOIF {if ([06:00-09:00] and [sensor:brightness] < 40) {fhem_set"lamp:FILTER=STATE!=on on"} else {fhem_set"lamp:FILTER=STATE!=off off"}} }

Bemerkung: Im Gegensatz zum FHEM-Modus arbeitet der Perl-Modus ohne Auswertung des eigenen Status (Zustandsauswertung), daher muss der Anwender selbst darauf achten, wiederholende Ausführungen zu vermeiden (im oberen Beispiel z.B. mit FILTER-Option). Elegant lässt sich das Problem der wiederholenden Ausführung bei zyklisch sendenden Sensoren mit Hilfe des Attributes DOIF_Readings lösen.

Es können beliebig viele Ereignisblöcke definiert werden, die unabhängig von einander durch einen oder mehrere Trigger ausgewertet und zur Ausführung führen können:

DOIF { if (<Bedingung mit Triggern>) ... } { if (<Bedingung mit Triggern>) ... } ...

Einzelne Ereignis-/Zeittrigger, die nicht logisch mit anderen Bedingungen oder Triggern ausgewertet werden müssen, können auch ohne if-Anweisung angegeben werden, z. B.:

DOIF {["lamp:on"];...} {[08:00];...} ...

Ereignis-/Zeittrigger sind intern Perlfunktionen, daher können sie an beliebiger Stelle im Perlcode angegeben werden, wo Perlfunktionen vorkommen dürfen, z. B.:

DOIF {Log 1,"state of lamp: ".[lamp:state]}

DOIF {fhem_set("lamp ".[remote:state])}

Es sind beliebige Hierarchietiefen möglich:

DOIF { if (<Bedingung>) {

   if (<Bedingung>) {
     if (<Bedingung mit Triggern>...
       ...
     }
   }
 }

}

Bemerkung: Innerhalb eines Ereignisblocks muss mindestens ein Trigger definiert werden, damit der gesamte Block beim passenden Trigger ausgeführt wird.

Eigene Funktionen

Ein besonderer Block ist der Block namens subs. In diesem Block werden Perlfunktionen definiert, die innerhalb des DOIFs genutzt werden. Um eine möglichst hohe Kompatibilität zu Perl sicherzustellen, wird keine DOIF-Syntax in eckigen Klammern unterstützt, insb. gibt es keine Trigger, die den Block ausführen können.

Beispiel:

<syntaxhighlight lang="perl"> { DOIF subs { ## Definition von Perlfunktionen lamp_on und lamp_off

 sub lamp_on {
    fhem_set("lamp on");
    set_State("on");
 }
 sub lamp_off {
    fhem_set("lamp off");
    set_State("off");
 }

} {[06:00];lamp_on()} ## Um 06:00 Uhr wird die Funktion lamp_on aufgerufen {[08:00];lamp_off()} ## Um 08:00 Uhr wird die Funktion lamp_off aufgerufen }

Eigene Funktionen mit Parametern

Unter Verwendung von Funktionsparameter lassen sich Definitionen oft vereinfachen, das obige Beispiel lässt sich mit Hilfe nur einer Funktion kürzer wie folgt definieren:

<syntaxhighlight lang="perl"> { DOIF subs { ## Definition der Perlfunktion lamp

 sub lamp {
    my ($state)=@_;  # Variable $state mit dem Parameter belegen
    fhem_set("lamp $state");
    set_State($state);
 }

} {[06:00];lamp("on")} ## Um 06:00 Uhr wird die Funktion lamp mit Parameter "on" aufgerufen {[08:00];lamp("off")} ## Um 08:00 Uhr wird die Funktion lamp mit dem Parameter "off" aufgerufen }

Eigener Namensraum: DOIF

Der Namensraum im Perl-Modus ist gekapselt. Selbstdefinierte Funktionen im DOIF-Device können nicht bereits existierende Perlfunktionen in FHEM (Namensraum main) überschreiben. Funktionen aus dem Namensraum main müssen mit vorangestellem Doppelpunkt angegeben werden: ::<perlfunction>

Eigene Perlfunktionen, die in myutils ausgelagert sind, befinden sich ebenfalls im Namensraum main. Wenn sie ausschließlich in DOIF-Devices benutzt werden sollen, so kann am Anfang vor deren Definition in myutils "package DOIF;" angegeben werden. In diesen Fall sind auch diese Funktion im DOIF-Device bekannt - sie können dann ohne vorangestellten Doppelpunkt genutzt werden.

Folgende FHEM-Perlfunktionen wurden ebenfalls im DOIF-Namensraum definiert, sie können, wie gewohnt ohne Doppelpunkt genutzt werden:

fhem, Log, Log3, InternVal, InternalNum, OldReadingsVal, OldReadingsNum, OldReadingsTimestamp, ReadingsVal, ReadingsNum, ReadingsTimestamp, ReadingsAge, Value, OldValue, OldTimestamp, AttrVal, AttrNum

Spezifische Perl-Funktionen im Perl-Modus

FHEM set-Befehl ausführen: fhem_set(<content>), mit <content> Übergabeparameter des FHEM set-Befehls

Beispiel: Lampe ausschalten:

fhem_set("lamp off");

entspricht:

fhem("set lamp off");

Der Aufruf der fhem_set-Funktion ist performater, da das Parsen nach dem set-Befehl im Gegensatz zum Aufruf mit der Funktion fhem entfällt.

Ein beliebiges FHEM-Event absetzen: set_Event(<Event>)

Beispiel: Setze das Event "on":

set_Event("on");

Status setzen: set_State(<value>,<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger> ist optional, default ist 1

Beispiel: Status des eignen DOIF-Device auf "on" setzen:

set_State("on");

Status des eigenen DOIF-Devices holen: get_State()

Beispiel: Schalte lampe mit dem eigenen Status:

fhem_set("lamp ".get_State());

Reading des eigenen DOIF-Devices schreiben: set_Reading(<readingName>,<value>,<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger> ist optional, default ist 0

set_Reading("weather","cold");

Reading des eigenen DOIF-Devices holen: get_Reading(<readingName>)

Beispiel: Schalte Lampe mit dem Inhalt des eigenen Readings "dim":

fhem_set("lamp ".get_Reading("dim"));

Setzen mehrerer Readings des eigenen DOIF-Devices in einem Eventblock:

set_Reading_Begin() set_Reading_Update(<readingName>,<value>,<change>), <change> ist optional set_Reading_End(<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger>

Die obigen Funktionen entsprechen den FHEM-Perlfunktionen: readingsBegin, readingsBulkUpdate, readingsEndUpdate.

Beispiel:

Die Readings "temperature" und "humidity" sollen in einem Eventblock mit dem zuvor belegten Inhalt der Variablen $temp bzw. $hum belegt werden.

set_Reading_Begin; set_Reading_Update("temperature",$temp); set_Reading_Update("humidity",$hum); set_Reading_End(1);

Ausführungstimer

Mit Hilfe von Ausführungstimern können Perl-Anweisungen verzögert ein- oder mehrmalig ausgeführt werden. Im Gegensatz zum FHEM-Modus können beliebig viele Timer gleichzeitig genutzt werden. Ein Ausführungstimer wird mit einem Timer-Namen eindeutig definiert. Über den Timer-Namen kann die Restlaufzeit abgefragt werden, ebenfalls kann er vor seinem Ablauf gelöscht werden.

Timer setzen: set_Exec(<timerName>, <seconds>, <perlCode>, <paramRef>, <condition>)

<timerName>: beliebige Angabe, sie spezifiziert eindeutig einen Timer <seconds>: Verzögerungszeit, sie wird per eval-Perlbefehl ausgewertet und kann daher Perlcode zur Bestimmung der Verzögerungszeit beinhalten <perlCode>: Perl-Anweisung, die nach Ablauf der Verzögerungszeit per eval-Perlbefehl ausgeführt wird <paramRef>: wird unter <perlCode> eine Perlfunktion angegeben, so kann optional hier eine Referenz auf den Übergabeparameter der Perlfunktion angegeben werden <condition>: optionale Bedingung zur Wiederholung der Perl-Ausführung. Sie wird per eval-Perlbefehl vor dem Setzen des Timers und vor der Ausführung der Perl-Anweisung auf wahr geprüft. Bei falsch wird die Wiederholung der Ausführungskette beendet. Wird <condition> nicht angegeben, so erfolgt keine Wiederholung der Perl-Anweisung.

Wird set_Exec mit dem gleichen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.

Restzeit des Timers: get_Exec(<timerName>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers

Laufenden Timer löschen: del_Exec(<timerName>)

Anwendungsbeispiele für einmalige verzögerte Ausführung

Funktion namens "lamp" mit dem Übergabeparameter "on" 30 Sekunden verzögert aufrufen:

set_Exec("lamp_timer",30,'lamp("on")');

Lampe verzögert um 30 Sekunden ausschalten:

set_Exec("off_timer",30,'fhem_set("lamp off")');

Das Event "off" 30 Sekunden verzögert auslösen:

set_Exec("Event_timer",30,'set_Event("off")');

Funktion namens "check_shutters" wird mit einer Referenz auf ein zuvor definiertes Array namens "shutters" um 5 Sekunden verzögert ausgeführt:

set_Exec("check_timer",5,"check_shutters",\@shutters);

Anwendungsbeispiele mit bedingter Wiederholung einer Ausführung

Wenn Alarm ausgelöst wird, soll eine Benachrichtigung gesendet werden und alle 60 Sekunden wiederholt werden, solange Alarmanlage auf "on" steht.

defmod di_alarm DOIF {["alarm:on"];;fhem("send myphone alarm!");;set_Exec("timer",60,'fhem("send myphone alarm!")','ReadingsVal("alarm","state","on") eq "on"')}

Wenn Taster auslöst, Lampe auf on schalten und noch zwei mal im Abstand von einer Sekunde wiederholt auf on schalten.

defmod di_lamp_on DOIF {["button:on"];;fhem_set"lamp on";;set_Exec("timer",1,'fhem_set("lamp on")','$count < 2')}

alternativ:

defmod di_lamp_on DOIF {["button:on"];;set_Exec("timer",'$count == 0 ? 0 : 1','fhem_set("lamp on")','$count < 2')}

$count ist eine interne Variable, die beginnend mit 0 nach jedem Durchlauf um eins erhöht wird.

Wenn Fenster geöffnet wird, dann soll eine Benachrichtigung erfolgen, dabei soll die Benachrichtigung bis zu 10 mal jeweils um weitere 60 Sekunden verzögert werden: erste Benachrichtigung nach 5 Minuten, zweite Benachrichtigung nach weiteren 6 Minuten, dritte Benachrichtigung nach weiteren 7 Minuten usw.

<syntaxhighlight lang="perl"> { defmod di_window DOIF {if ([window:state] eq "open") {\

 set_Exec("timer",'300+$count*60','fhem("echo speak window open")','$count < 9')\

} else {\

 del_Exec("timer")\

}\ } }

Pumpe alle zwei Stunden im Abstand von fünf Minuten 3 mal einschalten, beim ersten mal für 60 Sekunden, beim zweiten mal für 80 Sekunden und beim dritten mal für 100 Sekunden.

<syntaxhighlight lang="perl"> { defmod di_pump DOIF {[08:00-20:00,+[2]:00];;set_Exec("timer",300,'fhem_set("pump on-for-timer ".60+$count*20)','$count < 3')} }

Hochdimmen beim Tastendruck im Sekundentakt.

<syntaxhighlight lang="perl"> { defmod di_dimm DOIF {["button:on"];;@{$_pct}=(10,35,65,80,90,100);;set_Exec("timer",1,'fhem_set"lamp pct ".$_pct[$count]','$count < 6')} }

$_pct ist hier ein Array mit Helligkeitswerten, auf das innerhalb des Devices zu jedem Zeitpunkt zugegriffen werden kann.


init-Block

Wird ein Ereignisblock mit dem Namen init benannt, so wird dieser Block beim Systemstart ausgeführt. Er bietet sich insb. an, um Device-Variablen des Moduls vorzubelegen.

Device-Variablen

Device-Variablen sind sogenannte Instanzvariablen, die global innerhalb eines DOIF-Devices genutzt werden können. Deren Inhalt bleibt von Trigger zu Trigger während der Laufzeit des System erhalten. Sie beginnen mit $_ und müssen nicht deklariert werden. Wenn sie nicht vorbelegt werden, gelten sie als nicht definiert. Das lässt sich abfragen mit:

if (defined $_...) ...

Instanzvariablen überleben nicht den Neustart, sie können jedoch z.B. im init-Block, der beim Systemstart ausgewertet wird, aus mit Inhalten aus Readings initialisiert werden.

Bsp. Initialisierunger einer Instanzvariablen namens $_status, beim Systemstart mit dem Status des Moduls:

init {$_status=get_State()}

Instanzvariablen lassen sich indizieren, z. B.:

my $i=0; $_betrag{$i}=100;

Ebenso funktionieren hash-Variablen z. B.: $_betrag{heute}=100;

Blockierende Funktionsaufrufe (blocking calls)

DOIF verwaltet blockierende Funktionsaufrufe, d.h. die in diesem Zusammenhang gestarteten FHEM-Instanzen werden gelöscht, beim Herunterfahren (shutdown), Wiedereinlesen der Konfiguration (rereadcfg) Änderung der Konfiguration (modify) und Deaktivieren des Gerätes (disabled).

Die Handhabung von blockierenden Funktionsaufrufen ist im FHEMwiki erklärt, s. Blocking Call.

Der von der Funktion BlockingCall zurückgegebene Datensatz ist unterhalb von $_blockingcalls abzulegen, z.B.

$_blockingcalls{<blocking call name>} = ::BlockingCall(<blocking function>, <argument>, <finish function>, <timeout>, <abort function>, <abort argument>) unless(defined($_blockingcalls{<blocking call name>}));

Für unterschiedliche blockierende Funktionen ist jeweils ein eigener Name (<blocking call name>) unterhalb von $_blockingcalls anzulegen.

Wenn <blocking function>, <finish function> und <abort function> im Package DOIF definiert werden, dann ist dem Funktionsnamen DOIF:: voranzustellen, im Aufruf der Funktion BlockingCall, z.B. DOIF::<blocking function>

$_blockingcalls ist eine für DOIF reservierte Variable und darf nur in der beschriebener Weise verwendet werden.

Nutzbare Attribute im Perl-Modus

addStateEvent checkReadingEvent DOIF_Readings disable event_Readings notexist readingList setList uiTable uiState weekdays readingFnAttributes


set-Befehle im Perlmodus set <name> disable blockiert die Befehlsausführung

set <name> enable aktiviert die Befehlsausführung

set <name> <Blockname> führt den entsprechenden DOIF-Block aus

Anwendungsbeispiele im Perlmodus

Treppenhauslicht mit Bewegungsmelder <syntaxhighlight lang="perl"> { defmod di_light DOIF {\

 if (["FS:motion"]) {                          # bei Bewegung\
   fhem_set("lamp on") if ([?lamp] ne "on");;  # Lampe einschalten, wenn sie nicht an ist\
   set_Exec("off",30,'fhem_set("lamp off")');; # Timer namens "off" für das Ausschalten der Lampe auf 30 Sekunden setzen bzw. verlängern\
 }\

} } Einknopf-Fernbedienung

Anforderung: Wenn eine Taste innerhalb von zwei Sekunden zwei mal betätig wird, soll der Rollladen nach oben, bei einem Tastendruck nach unten.

<syntaxhighlight lang="perl"> { defmod di_shutter DOIF {\

 if (["FS:^on$"] and !get_Exec("shutter")){          # wenn Taste betätigt wird und kein Timer läuft\
   set_Exec("shutter",2,'fhem_set("shutter down")');;#Timer zum shutter down auf zwei Sekunden setzen\
 } else {                                            # wenn Timer läuft, d.h. ein weitere Tastendruck innerhalb von zwei Sekunden\
   del_Exec("shutter");;                             # Timer löschen\
   fhem_set("shutter up");;                          # Rollladen hoch\
 }\

} } Aktion auslösen, wenn innerhalb einer bestimmten Zeitspanne ein Ereignis x mal eintritt

Im folgenden Beispiel wird die Nutzung von Device-Variablen demonstriert.

<syntaxhighlight lang="perl"> { defmod di_count DOIF {\

 if (["FS:on"] and !get_Exec("counter")) {                                        # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft\
   $_count=1;;                                                                    # setze count-Variable auf 1\
   set_Exec("counter",3600,'Log (3,"count: $_count action") if ($_count > 10)');; # setze Timer auf eine Stunde zum Protokollieren der Anzahl der Ereignisse, wenn sie über 10 ist\
 } else {\
   $_count++;;                                                                    # wenn Timer bereits läuft zähle Ereignis\
 }\

} }