DOIF/Perl-Modus: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
Zeile 1: | Zeile 1: | ||
Das DOIF-Modul arbeitet im DOIF-Perl-Modus, wie auch im DOIF-FHEM-Modus, ereignisgesteuert. Der DOIF-Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Ein Steuerungsablauf wird in Perl in DOIF-Perl-Blöcken programmiert. Innerhalb eines DOIF-Devices können mehrere Perl-Blöcke programmiert werden, die durch passende Event- oder Zeittrigger unabhängig von einanader zur Ausführung gebracht werden. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Es können innerhalb eines DOF-Devices eigene Perlfunktionen definiert und genutzt werden. Es werden ebenfalls sog. Templates unterstützt. Mit Hilfe eines Templates kann ein bestimmter Automatisierungsablauf für mehrere Szenarien in einem DOIF-Device erstellt werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt. Das garantiert zur Laufzeit eine maximal mögliche Performance. | Das DOIF-Modul arbeitet im DOIF-Perl-Modus, wie auch im DOIF-FHEM-Modus, ereignisgesteuert. Der DOIF-Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Ein Steuerungsablauf wird in Perl in DOIF-Perl-Blöcken programmiert. Innerhalb eines DOIF-Devices können mehrere Perl-Blöcke programmiert werden, die durch passende Event- oder Zeittrigger unabhängig von einanader zur Ausführung gebracht werden. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Es können innerhalb eines DOF-Devices eigene Perlfunktionen definiert und genutzt werden. Es werden ebenfalls sog. Templates (deutsch: Schablonen) unterstützt. Mit Hilfe eines Templates kann ein bestimmter Automatisierungsablauf für mehrere Szenarien in einem DOIF-Device erstellt werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt. Das garantiert zur Laufzeit eine maximal mögliche Performance. | ||
Diese Seite befindet sich im Aufbau. | Diese Seite befindet sich im Aufbau. |
Version vom 2. Januar 2022, 11:09 Uhr
Das DOIF-Modul arbeitet im DOIF-Perl-Modus, wie auch im DOIF-FHEM-Modus, ereignisgesteuert. Der DOIF-Perl-Modus ist sowohl für einfache, als auch für komplexere Automatisierungsabläufe geeignet. Ein Steuerungsablauf wird in Perl in DOIF-Perl-Blöcken programmiert. Innerhalb eines DOIF-Devices können mehrere Perl-Blöcke programmiert werden, die durch passende Event- oder Zeittrigger unabhängig von einanader zur Ausführung gebracht werden. Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Es können innerhalb eines DOF-Devices eigene Perlfunktionen definiert und genutzt werden. Es werden ebenfalls sog. Templates (deutsch: Schablonen) unterstützt. Mit Hilfe eines Templates kann ein bestimmter Automatisierungsablauf für mehrere Szenarien in einem DOIF-Device erstellt werden. Zum Zeitpunkt der Definition werden alle DOIF-spezifischen Angaben in Perl übersetzt. Das garantiert zur Laufzeit eine maximal mögliche Performance.
Diese Seite befindet sich im Aufbau.
Aufbau einer Definition im DOIF-Perl-Modus
Der Aufbau der Definition im DOIF-Perl-Modus wird in drei Bereiche unterteilt: DOIF-Perl-Blöcke, Template-Definitionen und Template-Aufrufe. Die letzten beiden Bereiche sind optional, die Reihenfolge der drei Bereiche ist frei wählbar.
define <name> DOIF
<DOIF-Perl-Blöcke>
<Template-Definitionen (optional)>
<Aufrufe zuvor definierter Templates (optional)>
Aufbau eines DOIF-Perl-Blocks
<Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>}
Ein DOIF-Perl-Block wird in Perl programmiert. Dabei muss man Ereignis-/Zeittrigger in einer DOIF-spezifischen Syntax in eckigen Klammern im Programmcode einsetzen. Bei der Definition werden diese Angaben vom Modul ausgewertet, es werden intern entsprechende Trigger aufgesetzt, anschließend werden die Trigger-Angaben gegen interne Perlfunktionen ersetzt. So entsteht intern ein reiner Perl-Block, der zur Laufzeit ausgeführt wird, wenn ein Ereignis im System auftritt, welches zu den definierten Triggern des Blocks passt. Es können beliebig viele von einander unabhängige Perl-Blöcke innerhalb eines DOIF-Devices definiert werden. Wird kein Name eines Perl-Blocks definiert, so wird der Block unter dem Namen block_<nr> intern geführt.
Beispiel
Definition eines DOIF-Perl-Blocks ohne Namen
defmod di_rc_tv DOIF \
{ ## Beginn eines Perlblocks ohne Namen\
if ([remotecontol:"on"]) \
{fhem_set"tv on"} \
else \
{fhem_set"tv off"}} \
} ## Ende des Perlblocks
Beispiel
Definition von zwei benannten DOIF-Perl-Blöcken
defmod di_clock_radio DOIF \
radio_on { ## Beginn eines Perlblocks namens radio_on\
if ([06:30|Mo Di Mi] or [08:30|Do Fr Sa So])\
{fhem_set"radio on"} \
} ## Ende des Perlblocks namens radio_on\
\
radio_off { ## Beginn eines Perlblocks namens radio_off\
if ([08:00|Mo Di Mi] or [09:30|Do Fr Sa So])\
{fhem_set"radio off"} \
} ## Ende des Perlblocks namens radio_off
Aufbau einer Template-Definition
DEF TPL_<Template-Name>(<DOIF-Block-Definition mit Platzhaltern: $1,$2,...>)
Die Definition eines Templates ist optional. Sie bietet sich dann an, wenn mehrere gleich aufgebaute Abläufe (hier Szenen) definiert werden sollen. Eine Template-Definition beginnt mit DEF gefolgt vom Namen des Templates welches mit TPL_ beginnen muss. Anschließend wird eingefasst in runde Klammern ein Perl-Block programmiert. Variable Stellen innerhalb des Perl-Blocks werden mit Platzhaltern: $1, $2, usw. angegeben. Diese werden beim Aufruf des Templates gegen konkrete Angaben ersetzt. Insbesondere können so verschiedene Ereignis- oder Zeittrigger in den zuvor programmierten Ablauf eingesetzt werden.
Beispiel
DEF TPL_light ( ## Definition des Templates namens TPL_light zur Steuerung einer Lichtszene\
light_$1 { ## DOIF Block namens light_<Szene>\
## Platzhalter für: Szenennamen Daemmerungssensor Zeitspanne on-Befehl(e) off-Befehl(e)\
## Nr. des Platzhalters: $1 $2 $3 $4 $5\
if (($3) and [$2] eq "on") { ## Innerhalb Zeitspanne (hier $3) und wenn Dämmerung (hier $2) gleich on\
fhem_set($4);; ## schalte on-Befehle\
set_State("Szene: $1");; ## Setze Status des DOIF-Moduls auf den Szenennamen\
} else {\
fhem_set ($5);; ## sonst, schalte off-Befehle\
}\
} ## Ende des Perl-Blocks\
) ## Ende der Templatedefinition\
Aufruf eines Templates
TPL_<Template-Name>(<Übergabeparameter für $1>,<Übergabeparameter für $2>,...>)
Ein Template wird durch die Angabe des zuvor definierten Template-Namens aufgerufen. Hinter dem Template-Namen werden in runden Klammern kommagetrennten Angaben für die Platzhalter $1, $2, usw. gemacht. Durch den Aufruf wird intern ein Perl-Block generiert, bei dem alle Platzhalter im definierten Perl-Block des Templates gegen konkrete Angaben getauscht wurden.
Beispiele
Das oben definierte Beispiel-Template wird für zwei verschiedene Szenarien benutzt. Hierbei entstehen mit dem im Template definierten Ablauf zwei Perl-Blöcke, die unabhängig von einander arbeiten.
TPL_light (Erdgeschoss,Daemmerung2,[06:25-08:00|8] or [15:00-23:00],"Lampekueche,LampeFlur on","Lampekueche,LampeFlur off")\
TPL_light (Kind2,Daemmerung2,[06:25-08:00|8] or [15:00-23:10],"schrank2 on","schrank2 off")
Ereignistrigger
In einem DOIF-Perl-Block sollte mindestens eine Ereignis- oder Zeittriggerangabe vorkommen. Diese Angabe ist erforderlich, damit der Perl-Block beim passenden Event oder der angegeben Zeit ausgeführt wird. Sie wird grundsätzlich in eckigen Klammern angegeben und wird bei der Definition intern gegen eine Perlfunktion ersetzt, die einen Rückgabewert bei ihrer Ausführung liefert.
Eventtrigger mit Inhalt
[<device>] reagiert auf alle Events vom angegebenen Device, Rückgabewert: STATE des Devices (nicht das Reading state des Devices)
Beispiel
define di_lamp DOIF {if ([lamp] eq "on) {fhem_set("push lamp on")}}
[<device>:<reading>] reagiert auf alle Events vom angegebenen Device mit dem angegebenen Reading, Rückgabewert: Inhalt des Readings des Devices
Beispiel
define di_lamp DOIF {if ([lamp:state] eq "on) {fhem_set("push lamp on")}}
Eventtrigger mit Rückgabewert wahr/falsch
Eventtrigger für ein bestimmtes Device
[<Device>:"<Regex-Events>"] reagiert auf alle Events vom angegebenen Device, Rückgabewert: wahr (true) zum Zeitpunkt des Events, wenn der Regex-Ausdruck passt, sonst falsch (false)
Beispiel
define di_button DOIF {if ([button:"^btn1:"]) {fhem_set("lamp on")}} # die if-Bedingung ist wahr und führt zur Ausführung, wenn vom Device button, das Event beginnend mit btn1: vorkommt
Eventtrigger für mehrere Devices
["<Regex-Devices>:<Regex-Events>"] reagiert auf Events mit Devices mit Events, die den jeweiligen Regex-Angaben entsprechen, Rückgabewert: wahr (true) zum Zeitpunkt des Events, sonst falsch (false)
Beispiel
define di_window DOIF {if (["^window:on$"]) {fhem_set("push window $device on")}} # die Bedingung ist wahr und führt zur Ausführung, wenn ein Devices beginnend mit window ein Event endend mit on liefert
Zeittrigger
Zeittrigger zu einer bestimmten Uhrzeit
[<time>] der Zeitpunkt-Trigger mit <time>: [HH:MM], [HH:MM:SS] triggert seinen Perl-Block zum angegebenen Zeitpunkt, Rückgabewert: wahr (true) zum Triggerzeitpunkt, sonst falsch (false)
Beispiel
define di_pump DOIF {if ([10:00] or [11:00:30]) {fhem_set(pump on-for-timer 300}} # um 10:00 Uhr und um 11:00:30 wird der entsprechende Perl-Block ausgeführt, die if-Bedingung ist zu diesen beiden Zeitpunkten wahr und führt zur Ausführung der Anweisung
Zeitintervalle
[<begin>-<end>] <begin>, <end> als HH:MM, HH:MM:SS, triggert seinen Perl-Block zu <begin>-Zeit und zu <end>-Zeit, das Intervall ist ab <begin>-Zeitpunkt wahr und ab <end>-Zeitpunkt falsch
Beispiel
define di_lamp_on_off DOIF {if ([10:00-22:00]) {set lamp on} else {set lamp off}} # um 10:00 Uhr und um 22:00 Uhr wird der entsprechende Perl-Block getriggert, die if-Bedingung ist ab 10:00 Uhr wahr, lamp wird eingeschaltet, um 22:00 uhr ist die if-Bedingung nicht mehr wahr, lamp wird ausgeschaltet
indirekte Zeitangaben
[[<device><reading>]] im angegebenen Reading muss eine gültige Zeitangabe als [HH:MM], [HH:MM:SS] stehen, um diese Zeit wird der entsprechende Perl-Block ausgeführt, die Bedingung ist dann wahr, sonst falsch
Beispiel
{if ([[mydevice:time]]) {set lamp on}} # zum Zeitpunkt, der sich im Reading time des Devices mydevice wird der Perl-Block getriggert, die if-Bedingung ist wahr und lamp wird eingeschaltet
{if ([[mydevice:from]-[mydevice:to]]) {set lamp on} else {set lamp off}} # im Zeitintervall from-to brennt lamp, sonst ist sie aus
relative Zeitangaben
Zyklischer Trigger im zeitlichen Abstand
[+<time>] <time> als HH:MM, HH:MM:SS oder Zahl in Sekunden
Beispiel
{[+00:30];fhem("save")} # sichern der Konfiguration alle 30 Minuten
ausgerichtete Zeittrigger
X-Minuten nach einer vollen Stunde
[:MM] mit MM sind Minutenangaben zwischen 00 und 59
Beispiel
{[:15];[:45];fhem_set("pump on-for-timer 300")} # Pumpe 15 und 45 Minuten nach einer vollen Stunde für 5 Minuten einschalten
Alle X-Minuten ab einer vollen Stunde
[+:MM] mit MM in Minuten als Teiler von 60, sinnvoll Angaben sind: 2, 3, 4, 5, 6, 10, 12, 15, 20, 30
Beispiel
{[+:15];fhem_set("pump on-for-timer 300")} # Pumpe um HH:00, HH:15, HH:30, HH:45 für fünf Minuten einschalten
X-Minuten nach einer vollen Stunde, alle X-Stunden
[+[h]:MM] mit MM in Minuten zwischen 1 und 59, h in Stunden als Teiler von 24, sinnvolle Angaben für h sind: 2, 4, 6, 12
Beispiel
{[+[4]:05]; fhem_set("pump on-for-timer 300")} # Pumpe um 00:05, 04:05, 08:05, 12:05, 16:05, 20:05 Uhr für fünf Minuten einschalten
Wochentagsteuerung
Zeittrigger an bestimmen Wochen-/Ferientagen
[<time-trigger>|0123456789] Bedeutung: 0 bis 6 für So. bis Sa., 7 Wochenende oder Feiertag, 8 Werktags, 9 Wochenende oder Feiertag am kommenden Tag
alternativ
[<time-trigger>|So Mo Di Mi Do Fr Sa WE AT MWE] WE entspricht der Ziffer 7, AT der Ziffer 8 und MWE der Ziffer 9
oder
[<time-trigger>|Su Mo Tu We Th Fr Sa WE WD TWE] WE entspricht der Ziffer 7, WD der Ziffer 8 und TWE der Ziffer 9
Beispiel
defmod di_lamp DOIF\
{[22:00|Sa];;fhem_set("lamp on")}\
{[08:00|Mo];;fhem_set("lamp off")}
Intervall-Timer
Zyklischer Zeittrigger innerhalb einer Zeitspanne
[<begin>-<end>,<relativ timer>] triggert zu den aus <relativ timer> berechneten Zeitpunkten im angegebenen Zeitintervall <begin>-<end>
Beispiel
defmod di_pump DOIF {[08:00-22:00,+:30];;fhem_set"pump on-for-timer 300"} # von 08:00 Uhr bis 22:00 Uhr wird Pumpe alle 30 Minuten eingeschaltet, erste Schaltpunkt ist um 08:00 Uhr, der letzte um 21:30 Uhr
Für Umsteiger vom DOIF-FHEM-Modus
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 always-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>"}}
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. 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 Perl-Blö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>) ... } ## erster Block
{ if (<Bedingung mit Triggern>) ... } ## zweiter Block
...
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.:
Beispiel
defmod di_einschalten DOIF\
{["Daemmerung:on"];fhem_set("lamp on")}\
{[08:00];fhem_set("radio on")}
Ereignis-/Zeittrigger sind intern Perlfunktionen, daher können sie an beliebiger Stelle im Perlcode angegeben werden, wo Perlfunktionen vorkommen dürfen.
Beispiele
defmod di_log DOIF {Log 1,"state of lamp: ".[lamp:state]}
defmod di_set_lamp DOIF {fhem_set("lamp ".[remote:state])}
Innerhalb eines Perlblocks sind beliebige Hierarchietiefen möglich, dabei kann der Trigger irgendwo innerhalb des Blocks vorkommen:
DOIF
{ if (<Bedingung>) {
if (<Bedingung>) {
if (<Bedingung mit Triggern>...
...
}
}
}
}
Bemerkung
Innerhalb eines DOIF-Perl-Blocks muss mindestens ein Trigger definiert werden. Zu beachten ist, dass beim passenden Trigger nicht nur ein Zweig, sondern der gesamte Block ausgeführt wird.
Eigene Funktionen
Ein besonderer Perl-Block ist der Block namens subs. In diesem Block werden Perlfunktionen definiert, die innerhalb des DOIFs genutzt werden, sie befinden sich im Package DOIF und können in den folgenden Perl-Blöcken benutzt werden. Um eine möglichst hohe Kompatibilität zu Perl sicherzustellen, wird im subs-Block keine DOIF-Syntax in eckigen Klammern unterstützt, insb. gibt es keine Trigger, die den Block ausführen können.
Beispiel
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:
Beispiel
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 DOIF-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 vorangestelltem 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
Die folgenden Perl-Funktionen befinden sich im Package DOIF, sie können in der DOIF-Definition unmittelbar genutzt werden.
FHEM-set-Befehl ausführen: fhem_set()
fhem_set(<content>); mit <content> Angaben des FHEM-set-Befehls
Beispiel
Lampe namens lamp ausschalten:
fhem_set("lamp off");
entspricht:
fhem("set lamp off");
Der Aufruf der fhem_set-Funktion ist performanter, da das Parsen nach dem set-Befehl mit Hilfe der Funktion fhem entfällt.
Ein FHEM-Event auslösen: set_Event()
set_Event(<Event>); mit <Event> das zu generierende FHEM-Event
Beispiel
Generiere das Event on:
set_Event("on");
Eigenen Status setzen: set_State()
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 auslesen: get_State()
Die Funktion besitzt keine Parameter.
Beispiel
Schalte Lampe namens lamp mit dem Status des eigenen DOIF-Devices:
fhem_set("lamp ".get_State());
Reading des eigenen DOIF-Devices setzen: set_Reading()
set_Reading(<readingName>,<value>,<trigger>); mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger> ist optional, default ist 0
Beispiel
set_Reading("weather","cold");
Reading des eigenen DOIF-Devices auslesen: get_Reading()
Beispiel
Schalte Lampe mit dem Inhalt des Readings namens 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);
Interne Timer
Mit Hilfe von Timern können Perl-Anweisungen verzögert ein- oder mehrmalig ausgeführt werden. Im Gegensatz zum wait-Timer im DOIF-FHEM-Modus, können beliebig viele Timer gleichzeitig genutzt werden. Ein Timer 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()
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 Timer-Namen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.
Anwendungsbeispiele für einmalige verzögerte Ausführung
Beispiel
Funktion namens lamp mit dem Übergabeparameter on 30 Sekunden verzögert aufrufen:
set_Exec("lamp_timer",30,'lamp("on")');
Beispiel
Lampe verzögert um 30 Sekunden ausschalten:
set_Exec("off_timer",30,'fhem_set("lamp off")');
Beispiel
Das Event off 30 Sekunden verzögert generieren:
set_Exec("Event_timer",30,'set_Event("off")');
Beispiel
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
Beispiel
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"')}
Beispiel
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 bei 0 nach jedem Durchlauf um eins erhöht wird.
Beispiel
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.
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")\
}\
}
Beispiel
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.
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')}
Beispiel
Hochdimmen beim Tastendruck im Sekundentakt.
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.
Restzeit des Timers bestimmen: get_Exec()
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()
del_Exec(<timerName>); <timeName> Name des Timers
init-Block
Wird ein DOIF-Perl-Block mit dem Namen init benannt, so wird dieser Block beim Systemstart von FHEM ausgeführt.
Beispiel
Lampe um 22:00 Uhr oder wenn das FHEM-System hochgefahren wird ausschalten:
defmod di_lamp_off DOIF init {[22:00];;fhem_set"lamp off"}
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 mit my deklariert werden. Intern handelt es sich um hash-Variablen des jeweiligen DOIF-Devices.
Beispiel
defmod di_var DOIF\
init {$_myvalue=10} ## nach dem Starten des FHEM-System Variable namens $_myvalue mit dem Wert 10 initialisieren\
{[+:30];;fhem_set("mydevice $_myvalue");;$_myvalue++} ## alle 30 Minuten set mydevice mit dem Wert von $_myvalue ausführen und diesen anschließend um eins erhöhen
Wenn eine Instanzvariable nicht initialisiert wird, gilt sie als nicht definiert. Das lässt sich mit dem Perlbefehl define abfragen.
Beispiel
if (!defined $_value) ($_value=30);
Instanzvariablen überleben nicht den Neustart, sie können jedoch z.B. im init-Block, der beim Systemstart von FHEM ausgewertet wird, mit Inhalten aus Readings initialisiert werden.
Beispiel
Initialisierung einer Instanzvariablen namens $_status beim Systemstart mit dem Status des Moduls:
init {$_status=get_State()}
Instanzvariablen lassen sich wie Perl-Hashes indizieren:
Beispiel
my $i=0;
$_betrag{$i}=100;
oder mit konkreten Angaben hier heute:
$_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
Folgende Auswahl an Attributen ist im DOIF-Perl-Modus nutzbar:
addStateEvent checkReadingEvent DOIF_Readings disable event_Readings notexist readingList setList uiTable uiState weekdays readingFnAttributes
Die Funktionsweise der Attribute kann im DOIF-FHEM-Modus nachgeschlagen werden.
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-Perl-Block unmittelbar aus
Einfache Anwendungsbeispiele
Treppenhauslicht mit Bewegungsmelder
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
Beispiel
Wenn eine Taste innerhalb von zwei Sekunden zwei mal betätig wird, soll der Rollladen nach oben, bei einem Tastendruck nach unten:
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 auftritt
Beispiel
defmod di_count DOIF {\
if (["FS:on"] and !get_Exec("counter")) { # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft\
$_my_count=1;; # setze count-Variable auf 1\
set_Exec("counter",3600,'Log (3,"count: $_my_count action") if ($_my_count > 10)');; # setze Timer auf eine Stunde zum Protokollieren der Anzahl der Ereignisse, wenn sie über 10 ist\
} else {\
$_my_count++;; # wenn Timer bereits läuft zähle Ereignis\
}\
}