DOIF/Perl-Modus: Unterschied zwischen den Versionen

Aus FHEMWiki
K (Tippfehler)
 
(321 dazwischenliegende Versionen von einem anderen Benutzer werden nicht angezeigt)
Zeile 1: Zeile 1:
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.
[[Datei:Licht-Szenarien.png|600px|rechts|Licht-Automatisierung]]


Syntax Perl-Modus:
Das DOIF-Modul arbeitet im DOIF-Perl-Modus, wie auch im DOIF-FHEM-Modus, ereignisgesteuert. Ein Steuerungsablauf wird in Perl in DOIF-Perl-Blöcken programmiert. Innerhalb eines DOIF-Devices können mehrere DOIF-Perl-Blöcke programmiert werden, die durch passende Event- oder Zeittrigger unabhängig von einander zur Ausführung gebracht werden.
 
Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Es können innerhalb eines DOIF-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.
 
Genauso wie der DOIF-FHEM-Modus, lässt sich der DOIF-Perl-Modus mit Hilfe des [https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg uiTable-Attributes] um eine Web-Oberfläche ergänzen. Auf diese Weise kann sowohl die Steuerung als auch die Benutzeroberfläche innerhalb eines Devices erstellt werden. In der Abbildung rechts sieht man eine [https://wiki.fhem.de/wiki/DOIF/Automatisierung#Helligkeitsabh.C3.A4ngige_Zeitsteuerung_f.C3.BCr_mehrere_Szenarien_mit_tabellarischer_.C3.9Cbersicht helligkeitsabhängige Lichtsteuerung für mehrere Szenarien] erstellt im DOIF-Perl-Modus mit einer Benutzeroberfläche.
 
==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.
 
<syntaxhighlight lang="perl">
define <name> DOIF
<DOIF-Perl-Blöcke>
<Template-Definitionen (optional)>
<Aufrufe zuvor definierter Templates (optional)>
</syntaxhighlight>
 
===Aufbau eines DOIF-Perl-Blocks===


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
define <name> DOIF <Template-Definitionen (optional)> <DOIF-Blöcke>
<Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern>}
</syntaxhighlight>
</syntaxhighlight>


Syntax Template-Definition:
Ein DOIF-Perl-Block wird in Perl programmiert. Dabei muss man [https://wiki.fhem.de/wiki/DOIF/Perl-Modus#Ereignistrigger Ereignis-]/[https://wiki.fhem.de/wiki/DOIF/Perl-Modus#Zeittrigger 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 voneinander 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 mit einem Ereignistrigger und einer Fallunterscheidung
 
<syntaxhighlight lang="perl">
 
defmod di_rc_tv DOIF        \
{                          ## Beginn eines Perlblocks ohne Namen\
  if ([remotecontol:"on"])  ## Das Device "remotecontrol" triggert das Modul, wenn im Event "on" vorkommt\
    {fhem_set"tv on"}      ## wird der Befehl "set tv on" ausgeführt\
  else                      ## sonst\
    {fhem_set"tv off"}      ## wenn im Event "on" nicht kommt, wird "set tv off" ausgeführt\
}                          ## Ende des Perlblocks
</syntaxhighlight>
 
'''Beispiel'''
 
Definition von zwei benannten DOIF-Perl-Blöcken mit Zeittriggern


<syntaxhighlight lang="perl">
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]) ## zu definierten Zeitpunkten wird der Block getriggert\
  {fhem_set"radio on"}    ## der Befehl "set radio on" wird ausgeführt\
}                          ## 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]) ## zu definierten Zeitpunkten wird der Block getriggert\
    {fhem_set"radio off"}  ## der Befehl "set radio off" wird ausgeführt\   
}                          ## Ende des Perlblocks namens radio_off
</syntaxhighlight>
===Aufbau einer Template-Definition===
{{Randnotiz|RNText='''nützliche Links'''
[https://wiki.fhem.de/wiki/DOIF/Templates DOIF-Templates]
}}
<syntaxhighlight lang="perl">
<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,...>)
</syntaxhighlight>
</syntaxhighlight>


Syntax DOIF-Block:
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'''
 
<syntaxhighlight lang="perl">
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\
</syntaxhighlight>
 
===Aufruf eines Templates===
 
<syntaxhighlight lang="perl">
TPL_<Template-Name>(<Übergabeparameter für $1>,<Übergabeparameter für $2>,...>)
</syntaxhighlight>
 
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.
 
<syntaxhighlight lang="perl">
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")
</syntaxhighlight>
 
===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 bei der Definition intern gegen eine Perlfunktion ersetzt, die einen Rückgabewert bei ihrer Ausführung liefert.
 
====Eventtrigger mit Inhalt====
'''Inhalt des Internals STATE'''
<syntaxhighlight lang="perl">
[<device>] reagiert auf alle Events vom angegebenen Device, Rückgabewert: STATE des Devices (nicht das Reading state des Devices)
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
define di_lamp DOIF {if ([lamp] eq "on) {fhem("push lamp on")}}
</syntaxhighlight>
'''Inhalt eines Readings'''
<syntaxhighlight lang="perl">
[<device>:<reading>] reagiert auf alle Events vom angegebenen Device mit dem angegebenen Reading, Rückgabewert: Inhalt des Readings des Devices
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
define di_lamp DOIF {if ([lamp:state] eq "on) {fhem("push lamp on")}}
</syntaxhighlight>
 
====Eventtrigger mit Rückgabewert wahr/falsch====
'''Eventtrigger für ein bestimmtes Device'''
<syntaxhighlight lang="perl">
[<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)
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
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
</syntaxhighlight>
'''Eventtrigger für mehrere Devices'''
<syntaxhighlight lang="perl">
["<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)
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
define di_window DOIF {if (["^window:on$"]) {fhem("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
</syntaxhighlight>
 
===Zeittrigger===
====Zeittrigger zu einer bestimmten Uhrzeit====
<syntaxhighlight lang="perl">
[<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)
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
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
 
</syntaxhighlight>
 
====Zeitintervalle====
<syntaxhighlight lang="perl">
[<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
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
define di_lamp_on_off DOIF {if ([10:00-22:00]) {fhem_set("lamp on")} else {fhem_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
</syntaxhighlight>
 
====indirekte Zeitangaben====
'''Zeittrigger, deren Zeitangabe in einem Reading gespeichert wird'''
<syntaxhighlight lang="perl">[[<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
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
{if ([[mydevice:time]]) {fhem_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]]) {fhem_set("lamp on")} else {fhem_set("lamp off")}} # im Zeitintervall from-to brennt lamp, sonst ist sie aus
</syntaxhighlight>
 
====relative Zeitangaben====
'''Zyklischer Trigger im zeitlichen Abstand'''
<syntaxhighlight lang="perl">
[+<time>] <time> als HH:MM, HH:MM:SS oder Zahl in Sekunden
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
{[+00:30];fhem("save")} # sichern der Konfiguration alle 30 Minuten
</syntaxhighlight>
 
====ausgerichtete Zeittrigger====
'''X-Minuten nach einer vollen Stunde'''
<syntaxhighlight lang="perl">
[:MM] mit MM sind Minutenangaben zwischen 00 und 59
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
{[:15];[:45];fhem_set("pump on-for-timer 300")} # Pumpe 15 und 45 Minuten nach einer vollen Stunde für 5 Minuten einschalten
</syntaxhighlight>
'''Alle X-Minuten ab einer vollen Stunde'''
<syntaxhighlight lang="perl">
[+:MM] mit MM in Minuten als Teiler von 60, sinnvolle Angaben sind: 02, 03, 04, 05, 06, 10, 12, 15, 20, 30
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
{[+:15];fhem_set("pump on-for-timer 300")} # Pumpe um HH:00, HH:15, HH:30, HH:45 für fünf Minuten einschalten
</syntaxhighlight>
'''X-Minuten nach einer vollen Stunde, alle X-Stunden'''
<syntaxhighlight lang="perl">
[+[h]:MM] mit MM in Minuten zwischen 1 und 59, h in Stunden als Teiler von 24, sinnvolle Angaben für h sind: 2, 3, 4, 6, 8, 12
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
{[+[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
</syntaxhighlight>
 
====Wochentagsteuerung====
'''Zeittrigger an bestimmen Wochen-/Ferientagen'''
<syntaxhighlight lang="perl">
[<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
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
defmod di_lamp DOIF\
{[22:00|Sa];;fhem_set("lamp on")}\
{[08:00|Mo];;fhem_set("lamp off")}
</syntaxhighlight>
 
====Intervall-Timer====
'''Zyklischer Zeittrigger innerhalb einer Zeitspanne'''
<syntaxhighlight lang="perl">
[<begin>-<end>,<relativ timer>] triggert zu den aus <relativ timer> berechneten Zeitpunkten im angegebenen Zeitintervall <begin>-<end>
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
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
</syntaxhighlight>
 
===Trigger unterbinden===
 
Soll eine Ereignis-/Zeitangabe nicht triggern, so lässt sich die Triggerung durch ein vorangestelltes Fragezeichen unterbinden.
 
<syntaxhighlight lang="perl">
[?<Triggerangabe>] <Triggerangabe> kann eine Ereignis- oder Zeitangabe sein
</syntaxhighlight>
 
<syntaxhighlight lang="perl">
defmod di_lamp DOIF \
{                                                    # hier triggert nur [darkness:state], dagegen wird das Zeitintervall nur abgefragt, \
  if ([darkness:state] eq "on" and [?15:00-22:00]) {  # es gibt keinen Trigger um 15:00 Uhr bzw. um 22:00 Uhr \
    fhem_set("lamp on") \     
  } else { \
    fhem_set("lamp off") \
  } \
}
</syntaxhighlight>
 
===Aggregationsfunktionen===
{{Randnotiz|RNText=Aggregationsfunktion
<syntaxhighlight lang="perl">
[<function>:<format>:"<regex device>:<regex event>":<reading>:<condition>,<default>]
 
<function>: #, @, #sum, #max, #min, #average, #median, @max, @min
# Anzahl der betroffenen Devices, @ kommagetrennte Liste Devices
#sum Summe, #max höchster Wert, #min niedrigster Wert, #average Durchschnitt, #median Medianwert
@max Device des höchsten Wertes, @min Device des niedrigsten Wertes
 
<format>: d<number>, a, s(<splittoken>)
d<number> zum Runden des Wertes mit Nachkommastellen, a für Aliasnamen bei Devicelisten, s(<splittoken>) <splittoken> sind Trennzeichen in der Device-Liste
 
"<regex Device>:<regex Event>" spezifiziert sowohl die betroffenen Devices, als auch den Ereignistrigger, die Syntax entspricht der DOIF-Syntax für Ereignistrigger.
 
<reading> konkretes Reading, welches überprüft werden soll
alternativ eine Regex in Anführungszeichen
"<regex reading>" Regex für Readings, die überprüft werden sollen
 
<condition> Aggregationsbedingung
wird <condition> in Anführungszeichen gesetzt, so werden Reading mit eq mit dem angegebenen Wert verglichen. Sonst kann ein Perlausdruck angegeben werden. Folgende Variablen sind vorbelegt und können im Perlausdruck benutzt werden:
 
$_ Inhalt des Readings
$number Nach Zahl gefilteres Reading
$name Name des Devices
$reading Name des Readings
$TYPE Devices-Typ
$STATE Status des Devices (nicht das Reading state)
$room Raum des Devices
$group Gruppe des Devices
 
<default> Default-Wert, falls kein Device gefunden wird, entspricht der Syntax des Default-Wertes bei Readingangaben
 
<format>, <reading>, <condition>, <default> sind optional
 
</syntaxhighlight>
}}
Mit Hilfe einer Aggregationsfunktion können mehrere Readings im System ausgewertet werden, die einem bestimmten Kriterium entsprechen. Die Filterkriterien können mit Hilfe von Regex-Ausdrücken für Devices, Events und Readings angegeben werden. Die Aggregationsangabe wirkt triggernd, soll sie nicht triggern, so muss ein Fragezeichen vorangestellt werden, siehe [https://wiki.fhem.de/wiki/DOIF/Perl-Modus#Trigger_unterbinden Trigger unterbinden]
 
<syntaxhighlight lang="perl">
[<function>:<format>:"<regex device>:<regex event>":<reading>:<condition>,<default>]
</syntaxhighlight>
 
'''Beispiele'''
 
Offene Fenster anzeigen. Wenn ein Device beginnend mit "window" im Namen ein Event sendet, dann wird bei allen Devices im System, deren Name mit "window" beginnt, status auf "open" geprüft. Diese werden im Status von di_windows angezeigt. Wird kein Device mit status "open" gefunden, so wird der Default-Wert "keine" übernommen.


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
<Blockname (optional)> {<Perlcode mit Ereignis-/Zeittriggern in eckigen Klammern>}
defmod di_windows DOIF init {set_State([@:"^window":state:"open","keine"])}
</syntaxhighlight>
</syntaxhighlight>


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.
Durchschnittstemperatur aller Temperatursensoren in der Gruppe "livingroom" gerundet auf 2 Nachkommastellen. Wenn ein Event von irgendeinem Device beginnend mit "temperature" auftritt, dann werden alle Devices im System durchsucht, die ein Reading namens "temperature" besitzen und sich in der Gruppe "livingroom" befinden.  


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.
<syntaxhighlight lang="perl">
defmod di_average_temp DOIF init {set_State ([#average:d2:":^temperature":temperature:$group eq "livingroom"])}
</syntaxhighlight>


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.
Um 18:00 Uhr werden alle Devices im System durchsucht, deren Reading "battery" nicht "ok" ist. Damit die Aggregationsangabe den DOIF-Perl-Block nicht triggert, muss am Anfang der Angabe ein Fragezeichen vorangestellt werden.


Definitionen im FHEM-Modus mit do-Attribut der Form:
<syntaxhighlight lang="perl">
defmod di_batterie_not_ok DOIF init {[18:00];;set_State([?@:":battery":battery:$_ ne "ok","alle OK"])}
</syntaxhighlight>


Um 18:00 Uhr werden alle Devices im System durchsucht, die ein Reading namens "Battery" oder "battery" haben. Bei diesen Readings wird die letzte Aktualisierung geprüft. Wenn sie älter als 24 Stunden ist, dann wird dessen Device in die Liste aufgenommen. Im Status des Moduls befinden sich danach alle Devices, deren Batterie-Reading länger nicht aktualisiert wurde.
<syntaxhighlight lang="perl">
defmod di_batteriecheck DOIF init {[18:00];;set_State([?@:"":"^[Bb]attery$":ReadingsAge($name,$reading,0) > 3600*24,"keine"])}
</syntaxhighlight>
Das Modul wird getriggert wenn ein beliebiges Device ein Event beginnend mit "temperature" sendet, es werden daraufhin alle Devices nach dem Reading "temperature" durchsucht und der maximale/minimale Wert im Reading max/min von di_min_max gespeichert.
<syntaxhighlight lang="perl">
defmod di_min_max DOIF\
{set_Reading("max",[#max:":^temperature":temperature])}\
{set_Reading("min",[#min:":^temperature":temperature])}
</syntaxhighlight>
==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:
<syntaxhighlight lang="perl">
DOIF (<Bedingung mit Trigger>) (<FHEM-Befehle>) DOELSE (<FHEM-Befehle>)
DOIF (<Bedingung mit Trigger>) (<FHEM-Befehle>) DOELSE (<FHEM-Befehle>)
</syntaxhighlight>


lassen sich wie folgt in Perl-Modus übertragen:
lassen sich wie folgt in Perl-Modus übertragen:


DOIF {if (<Bedingung mit Trigger>) {fhem"<FHEM-Befehle>"} else {fhem"<FHEM-Befehle>"}}
<syntaxhighlight lang="perl">
DOIF {if (<Bedingung mit Trigger>) {fhem("<FHEM-Befehle>")} else {fhem("<FHEM-Befehle>")}}
</syntaxhighlight>


Die Bedingungen des FHEM-Modus können ohne Änderungen in Perl-Modus übernommen werden.
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.


'''Einfache Anwendungsbeispiele''' (vgl. Anwendungsbeispiele im FHEM-Modus):
'''Beispiel'''
<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"}}
Heizung soll bei einer Temperatur unter 21 Grad eingeschaltet werden, sonst soll sie ausgeschaltet werden. Das Unterschreiten bzw. Überschreiten der Wunschtemperatur wird über die Definition des Readings '''cold''' realisiert. Obwohl der Temperatursensor zyklisch seine Daten sendet, triggert das Reading '''cold''' das eigene Modul und führt zum Schalten der Heizung nur wenn sich sein Zustand (on/off) ändert.


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"}}
<syntaxhighlight lang="perl">
defmod di_Threshold DOIF {                       \
if ([$SELF:cold] eq "on") { # wenn es kalt ist\
  fhem_set("heating on");;  # Heizung einschalten\
  set_State ("heating");;    # Status auf 'heating' setzen\
} else {                     # sonst\
  fhem_set("heating off");;  # Heizung ausschalten\
  set_State ("not heating");;# Status auf 'not heating' setzen\
} \
}
attr di_Threshold DOIF_Readings cold: ([room:temperature] < 21 ? "on" : "off")
</syntaxhighlight>
</syntaxhighlight>


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:
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:


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
DOIF
DOIF
{ if (<Bedingung mit Triggern>) ... }
{ if (<Bedingung mit Triggern>) ... } ## erster Block
{ if (<Bedingung mit Triggern>) ... }
{ if (<Bedingung mit Triggern>) ... } ## zweiter Block
...
...
</syntaxhighlight>
</syntaxhighlight>


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.:
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'''
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
DOIF
defmod di_einschalten DOIF\
{["lamp:on"];...}
{["Daemmerung:on"];;fhem_set("lamp on")}\
{[08:00];...}
{[08:00];;fhem_set("radio on")}
...
</syntaxhighlight>
</syntaxhighlight>


Ereignis-/Zeittrigger sind intern Perlfunktionen, daher können sie an beliebiger Stelle im Perlcode angegeben werden, wo Perlfunktionen vorkommen dürfen, z. B.:
Ereignis-/Zeittrigger sind intern Perlfunktionen, daher können sie an beliebiger Stelle im Perlcode angegeben werden, wo Perlfunktionen vorkommen dürfen.
 
'''Beispiele'''
 
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
DOIF {Log 1,"state of lamp: ".[lamp:state]}
defmod di_log DOIF {Log 1,"state of lamp: ".[lamp:state]}


DOIF {fhem_set("lamp ".[remote:state])}
defmod di_set_lamp DOIF {fhem_set("lamp ".[remote:state])}
</syntaxhighlight>
</syntaxhighlight>


Es sind beliebige Hierarchietiefen möglich:
Innerhalb eines Perlblocks sind beliebige Hierarchietiefen möglich, dabei kann der Trigger irgendwo innerhalb des Blocks vorkommen:


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
Zeile 84: Zeile 412:
</syntaxhighlight>
</syntaxhighlight>


Bemerkung: Innerhalb eines Ereignisblocks muss mindestens ein Trigger definiert werden, damit der gesamte Block beim passenden Trigger ausgeführt wird.
'''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==
==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.
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:
'''Beispiel'''


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
Zeile 107: Zeile 437:
</syntaxhighlight>
</syntaxhighlight>


Eigene Funktionen mit Parametern
'''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:
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'''


<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
Zeile 125: Zeile 457:
==Eigener Namensraum: DOIF==
==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 vorangestelltem Doppelpunkt angegeben werden: '''::<perlfunction>'''
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>'''. Variablen aus dem Namensraum '''main''' werden wie folgt angegeben: '''$::<Variablenname>'''-


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.
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.
Zeile 131: Zeile 463:
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:


'''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, OldReadingsAge, ReadingsVal, ReadingsNum, ReadingsTimestamp, ReadingsAge, Value, OldValue, OldTimestamp, AttrVal, AttrNum
'''
'''


==Spezifische Perl-Funktionen im Perl-Modus==
==Spezifische Perl-Funktionen im Perl-Modus==
===FHEM set-Befehl ausführen: '''fhem_set()'''===
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()'''===
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
fhem_set(<content>), mit <content> Übergabeparameter des FHEM set-Befehls
fhem_set(<content>); mit <content> Angaben des FHEM-set-Befehls
</syntaxhighlight>
</syntaxhighlight>


Beispiel: Lampe ausschalten:
'''Beispiel'''


Lampe namens '''lamp''' ausschalten:
<syntaxhighlight lang="perl">
fhem_set("lamp off");
fhem_set("lamp off");
</syntaxhighlight>


entspricht:
entspricht:


<syntaxhighlight lang="perl">
fhem("set lamp off");
fhem("set lamp off");
</syntaxhighlight>


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.
Der Aufruf der fhem_set-Funktion ist performanter, da das Parsen nach dem set-Befehl mit Hilfe der Funktion '''fhem''' entfällt.
 
===Ein Event auslösen: '''set_Event()'''===
 
Ein beliebiges FHEM-Event generieren: set_Event(<Event>)


===Ein FHEM-Event auslösen: '''set_Event()'''===
<syntaxhighlight lang="perl">
set_Event(<Event>); mit <Event> das zu generierende FHEM-Event
</syntaxhighlight>
'''Beispiel'''
'''Beispiel'''


Generiere das Event "on":
Generiere das Event '''on''':


<syntaxhighlight lang="perl">
set_Event("on");
set_Event("on");
</syntaxhighlight>


===Status setzen: '''set_State()'''===
===Eigenen Status setzen: '''set_State()'''===
<syntaxhighlight lang="perl">
set_State(<value>,<trigger>); mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger> ist optional, default ist 1
</syntaxhighlight>


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


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


<syntaxhighlight lang="perl">
set_State("on");
set_State("on");
</syntaxhighlight>


===Status des eigenen DOIF-Devices auslesen: '''get_State()'''===
===Status des eigenen DOIF-Devices auslesen: '''get_State()'''===
Die Funktion besitzt keine Parameter.


Beispiel: Schalte lampe mit dem eigenen Status:
'''Beispiel'''


Schalte Lampe namens '''lamp''' mit dem Status des eigenen DOIF-Devices:
<syntaxhighlight lang="perl">
fhem_set("lamp ".get_State());
fhem_set("lamp ".get_State());
</syntaxhighlight>


===Reading des eigenen DOIF-Devices setzen: set_Reading()===
===Reading des eigenen DOIF-Devices setzen: '''set_Reading()'''===
 
<syntaxhighlight lang="perl">
set_Reading(<readingName>,<value>,<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger> ist optional, default ist 0
set_Reading(<readingName>,<value>,<Event>); mit <Event>: 0 ohne Eventgenerierung, 1 mit Eventgenerierung, <Event> ist optional, default ist 0
 
</syntaxhighlight>
'''Beispiel'''
<syntaxhighlight lang="perl">
set_Reading("weather","cold");
set_Reading("weather","cold");
</syntaxhighlight>


===Reading des eigenen DOIF-Devices auslesen: '''get_Reading()'''===
===Reading des eigenen DOIF-Devices auslesen: '''get_Reading()'''===
Zeile 184: Zeile 538:
'''Beispiel'''
'''Beispiel'''


Schalte Lampe mit dem Inhalt des eigenen Readings namens '''dim''':
Schalte Lampe mit dem Inhalt des Readings namens '''dim''':
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
fhem_set("lamp ".get_Reading("dim"));
fhem_set("lamp ".get_Reading("dim"));
Zeile 191: Zeile 545:
===Setzen mehrerer Readings des eigenen DOIF-Devices in einem Eventblock===
===Setzen mehrerer Readings des eigenen DOIF-Devices in einem Eventblock===
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
set_Reading_Begin()
set_Reading_Begin();
set_Reading_Update(<readingName>,<value>,<change>), <change> ist optional
set_Reading_Update(<readingName>,<value>,<change>); <change> ist optional
set_Reading_End(<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Trigger, <trigger>
set_Reading_End(<Event>); mit <Event>: 0 ohne Eventgenerierung, 1 mit Eventgenerierung
</syntaxhighlight>
</syntaxhighlight>


Zeile 200: Zeile 554:
'''Beispiel'''
'''Beispiel'''


Die Readings '''temperature''' und '''humidity''' sollen in einem Eventblock mit dem zuvor belegten Inhalt der Variablen '''$temp''' bzw. '''$hum''' belegt werden.
Die Readings '''temperature''' und '''humidity''' sollen in einem Eventblock mit dem zuvor belegten Inhalt der Variablen '''$temp''' bzw. '''$hum''' belegt werden:
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
set_Reading_Begin;
set_Reading_Begin;
Zeile 208: Zeile 562:
</syntaxhighlight>
</syntaxhighlight>


==Ausführungstimer==
==Interne Timer==


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 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(<timerName>, <seconds>, <perlCode>, <paramRef>, <condition>)
===Timer setzen: '''set_Exec()'''===
<syntaxhighlight lang="perl">
set_Exec(<timerName>, <seconds>, <perlCode>, <paramRef>, <condition>);


<timerName>: beliebige Angabe, sie spezifiziert eindeutig einen Timer
<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
<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
<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
<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.
<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.
</syntaxhighlight>


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 Timer-Namen '''<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
===Anwendungsbeispiele für einmalige verzögerte Ausführung===


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


Anwendungsbeispiele für einmalige verzögerte Ausführung
Funktion namens '''lamp''' mit dem Übergabeparameter '''on''' 30 Sekunden verzögert aufrufen:


Funktion namens "lamp" mit dem Übergabeparameter "on" 30 Sekunden verzögert aufrufen:
<syntaxhighlight lang="perl">
set_Exec("lamp_timer",30,'lamp("on")');
</syntaxhighlight>


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


Lampe verzögert um 30 Sekunden ausschalten:
Lampe verzögert um 30 Sekunden ausschalten:


<syntaxhighlight lang="perl">
set_Exec("off_timer",30,'fhem_set("lamp off")');
set_Exec("off_timer",30,'fhem_set("lamp off")');
</syntaxhighlight>


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


Das Event '''off''' 30 Sekunden verzögert generieren:
<syntaxhighlight lang="perl">
set_Exec("Event_timer",30,'set_Event("off")');
set_Exec("Event_timer",30,'set_Event("off")');
</syntaxhighlight>


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


Funktion namens '''check_shutters''' wird mit einer Referenz auf ein zuvor definiertes Array namens '''shutters''' um 5 Sekunden verzögert ausgeführt:
<syntaxhighlight lang="perl">
set_Exec("check_timer",5,"check_shutters",\@shutters);
set_Exec("check_timer",5,"check_shutters",\@shutters);
</syntaxhighlight>
===Anwendungsbeispiele mit bedingter Wiederholung einer Ausführung===


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.
Wenn Alarm ausgelöst wird, soll eine Benachrichtigung gesendet werden und alle 60 Sekunden wiederholt werden, solange Alarmanlage auf '''on''' steht:


<syntaxhighlight lang="perl">
defmod di_alarm DOIF {["alarm:on"];;fhem("send myphone alarm!");;set_Exec("timer",60,'fhem("send myphone alarm!")','ReadingsVal("alarm","state","on") eq "on"')}
defmod di_alarm DOIF {["alarm:on"];;fhem("send myphone alarm!");;set_Exec("timer",60,'fhem("send myphone alarm!")','ReadingsVal("alarm","state","on") eq "on"')}
</syntaxhighlight>


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


Wenn Taster auslöst, Lampe auf '''on''' schalten und noch zwei mal im Abstand von einer Sekunde wiederholt auf '''on''' schalten.
<syntaxhighlight lang="perl">
defmod di_lamp_on DOIF {["button:on"];;fhem_set"lamp on";;set_Exec("timer",1,'fhem_set("lamp on")','$count < 2')}
defmod di_lamp_on DOIF {["button:on"];;fhem_set"lamp on";;set_Exec("timer",1,'fhem_set("lamp on")','$count < 2')}
</syntaxhighlight>


alternativ:
alternativ:


<syntaxhighlight lang="perl">
defmod di_lamp_on DOIF {["button:on"];;set_Exec("timer",'$count == 0 ? 0 : 1','fhem_set("lamp on")','$count < 2')}
defmod di_lamp_on DOIF {["button:on"];;set_Exec("timer",'$count == 0 ? 0 : 1','fhem_set("lamp on")','$count < 2')}
</syntaxhighlight>
'''$count''' ist eine interne Variable, die beginnend bei 0 nach jedem Durchlauf um eins erhöht wird.


$count ist eine interne Variable, die beginnend mit 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.
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.
Zeile 270: Zeile 654:
}
}
</syntaxhighlight>
</syntaxhighlight>
'''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.
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.
Zeile 276: Zeile 662:
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')}
</syntaxhighlight>
</syntaxhighlight>
'''Beispiel'''


Hochdimmen beim Tastendruck im Sekundentakt.
Hochdimmen beim Tastendruck im Sekundentakt.
Zeile 283: Zeile 671:
</syntaxhighlight>
</syntaxhighlight>


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


===Restzeit des Timers bestimmen: '''get_Exec()'''===
<syntaxhighlight lang="perl">
get_Exec(<timerName>); Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers
</syntaxhighlight>
===Laufenden Timer löschen: '''del_Exec()'''===
<syntaxhighlight lang="perl">
del_Exec(<timerName>); <timeName> Name des Timers
</syntaxhighlight>


==init-Block==
==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 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:
 
<syntaxhighlight lang="perl">
defmod di_lamp_off DOIF init {[22:00];;fhem_set("lamp off")}
</syntaxhighlight>


==Device-Variablen==
==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 mit '''my''' deklariert werden. Intern handelt es sich um hash-Variablen des jeweiligen DOIF-Devices.


if (defined $_...) ...
'''Beispiel'''


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.
<syntaxhighlight lang="perl">
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
</syntaxhighlight>


Bsp. Initialisierunger einer Instanzvariablen namens $_status, beim Systemstart mit dem Status des Moduls:
Wenn eine Instanzvariable nicht initialisiert wird, gilt sie als nicht definiert. Das lässt sich mit dem Perlbefehl '''define''' abfragen.


'''Beispiel'''
<syntaxhighlight lang="perl">
if (!defined $_value) ($_value=30);
</syntaxhighlight>
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:
<syntaxhighlight lang="perl">
init {$_status=get_State()}
init {$_status=get_State()}
</syntaxhighlight>


Instanzvariablen lassen sich indizieren, z. B.:
Instanzvariablen lassen sich wie Perl-Hashes indizieren:


'''Beispiel'''
<syntaxhighlight lang="perl">
my $i=0;
my $i=0;
$_betrag{$i}=100;
$_betrag{$i}=100;
</syntaxhighlight>
oder mit konkreten Angaben hier '''heute''':


Ebenso funktionieren hash-Variablen z. B.:
<syntaxhighlight lang="perl">
$_betrag{heute}=100;
$_betrag{heute}=100;
</syntaxhighlight>


==Blockierende Funktionsaufrufe (blocking calls)==
==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''').


Die Handhabung von blockierenden Funktionsaufrufen ist im FHEMwiki erklärt, s. Blocking Call.
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.
Der von der Funktion BlockingCall zurückgegebene Datensatz ist unterhalb von '''$_blockingcalls''' abzulegen, z.B.


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


Für unterschiedliche blockierende Funktionen ist jeweils ein eigener Name (<blocking call name>) unterhalb von $_blockingcalls anzulegen.
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>
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.
'''$_blockingcalls''' ist eine für DOIF reservierte Variable und darf nur in der beschriebener Weise verwendet werden.


==Nutzbare Attribute im Perl-Modus==
==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
'''addStateEvent  checkReadingEvent  DOIF_Readings  disable  event_Readings  notexist  readingList  setList  uiTable  uiState  weekdays readingFnAttributes'''
readingFnAttributes


Die Funktionsweise der Attribute kann im DOIF-FHEM-Modus nachgeschlagen werden.
==set-Befehle im Perl-Modus==
===set <name> disable===


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


set <name> enable
===set <name> enable===
 
aktiviert die Befehlsausführung
aktiviert die Befehlsausführung


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


==Anwendungsbeispiele im Perlmodus==
führt den entsprechenden DOIF-Perl-Block unmittelbar aus
==FOR-Schleife==
Mit Hilfe der FOR-Schleife kann ein Template in einer Schleife mehrfach aufgerufen werden. Damit können mit einer Parameterliste gleiche Abläufe für verschiedene Szenarien erstellt werden.
<syntaxhighlight lang="perl">
FOR (<Array>,<Template>($_$1,$_$2,...)) mit $_$1 erstes Element des Arrays, $_$2 zweites Element des Arrays usw. der jeweiligen Zeile
</syntaxhighlight>
 
'''Beispiel'''
 
Lichtsteuerung für mehrere Szenarien


Treppenhauslicht mit Bewegungsmelder
<syntaxhighlight lang="perl">
defmod di_light DOIF subs  {\
## In einem Array namens $_sc wird pro Szenario eine Definitionszeile mit Parametern erstellt \
##            Zeitspanne                          Einschaltbefehl              Ausschaltbefehl\
push (@{$_sc},["[06:25-08:00|8] or [18:00-23:30]", "Lampekueche,Lampeflur on", "Lampekueche,Lampeflur off"]);;\
push (@{$_sc},["[06:25-08:00|8] or [18:00-22:10]", "schrank2 on",              "schrank2 off"]);;\
## Das Array kann um weitere Szenarien mit Hilfe des push-Befehls erweitert werden \
} \
## Template zur Steuerung des Lichtes für ein Szenario\
DEF TPL_light ( \
{                  ## Perl-Block \
  if ($1) {        ## In der Zeitspanne \
    fhem_set("$2")  ## Licht einschalten \
  } else { \
    fhem_set("$3")  ## Licht ausschalten \
  } \
}                  ## Ende des Perl-Blocks \
)                  ## Ende von TPL_light \
\
## Mit Hilfe der FOR-Schleife wird das Array $_sc durchlaufen, für jede Szene (Zeile des Arrays) wird das oben definierte Template mit den Parametern der Szene: Zeitspanne $_$1, Einschaltbefehl $_$2 und Ausschaltbefehl $_$2 aufgerufen \
\
FOR (@{$_sc},TPL_light($_$1,$_$2,$_$3)) \
\
## Die FOR-Schleife ruft auf: \
## TPL_light("[06:25-08:00|8] or [18:00-23:30]", "Lampekueche,Lampeflur on", "Lampekueche,Lampeflur off") \
## TPL_light("[06:25-08:00|8] or [18:00-22:10]", "schrank2 on",              "schrank2 off")
</syntaxhighlight>
 
Bei dieser Vorgehensweise können weitere Szenarien durch Hinzufügen einer Array-Zeile erstellt werden, ohne den restlichen Code anpassen zu müssen. Desweitern können die Elemente des Arrays für Visualisierungszwecke über das [https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg uiTable-Attribut] genutzt werden, siehe: [https://wiki.fhem.de/wiki/DOIF/Automatisierung#Helligkeitsabh.C3.A4ngige_Zeitsteuerung_f.C3.BCr_mehrere_Szenarien_mit_tabellarischer_.C3.9Cbersicht Helligkeitsabhängige Lichtsteuerung mit Web-Oberfläche]
 
==Einfache Anwendungsbeispiele==
 
===Treppenhauslicht mit Bewegungsmelder===
<syntaxhighlight lang="perl">
<syntaxhighlight lang="perl">
defmod di_light DOIF {\
defmod di_light DOIF {\
Zeile 354: Zeile 829:
</syntaxhighlight>
</syntaxhighlight>


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.
'''Beispiel'''


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">
<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 370: Zeile 845:
</syntaxhighlight>
</syntaxhighlight>


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 auftritt'''


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


<syntaxhighlight lang="perl">
<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\
     $_count=1;;                                                                    # setze count-Variable auf 1\
     $_my_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\
     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 {\
   } else {\
     $_count++;;                                                                    # wenn Timer bereits läuft zähle Ereignis\
     $_my_count++;;                                                                    # wenn Timer bereits läuft zähle Ereignis\
   }\
   }\
}
}
</syntaxhighlight>
</syntaxhighlight>
==Weitere Anwendungsbeispiele im DOIF-Perl-Modus==
===[https://wiki.fhem.de/wiki/DOIF/Automatisierung#Helligkeitsabh.C3.A4ngige_Zeitsteuerung_f.C3.BCr_mehrere_Szenarien Lichtsteuerung für mehrere Szenarien]===
===[https://wiki.fhem.de/wiki/DOIF/Automatisierung#Zeitgesteuerte_Heizungsregelung_mit_Hilfe_von_Raumthermostaten Steuerung von Raumthermostaten für mehrere Räume]===
===[https://wiki.fhem.de/wiki/DOIF/Automatisierung#Beschattungssteuerung_abh.C3.A4ngig_von_der_Zimmertemperatur_und_Sonneneinstrahlung_f.C3.BCr_mehrere_Szenarien Beschattungssteuerung für mehrere Szenarien]===
===[https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg#Anzahl_der_Tage_bis_zur_Abfall-Entsorgung Abfallentsorgung]===
===[https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg#Visualisierung_und_Steuerung:_Heiztherme Heiztherme]===
===[https://wiki.fhem.de/wiki/DOIF/uiTable_Schnelleinstieg#Visualisierung:_Anwesenheitsstatus Anwesenheitsstatus]===
[[Kategorie:HOWTOS]]

Aktuelle Version vom 9. März 2024, 12:36 Uhr

Licht-Automatisierung

Das DOIF-Modul arbeitet im DOIF-Perl-Modus, wie auch im DOIF-FHEM-Modus, ereignisgesteuert. Ein Steuerungsablauf wird in Perl in DOIF-Perl-Blöcken programmiert. Innerhalb eines DOIF-Devices können mehrere DOIF-Perl-Blöcke programmiert werden, die durch passende Event- oder Zeittrigger unabhängig von einander zur Ausführung gebracht werden.

Die Abläufe lassen sich, wie in höheren Programmiersprachen üblich, strukturiert programmieren. Es können innerhalb eines DOIF-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.

Genauso wie der DOIF-FHEM-Modus, lässt sich der DOIF-Perl-Modus mit Hilfe des uiTable-Attributes um eine Web-Oberfläche ergänzen. Auf diese Weise kann sowohl die Steuerung als auch die Benutzeroberfläche innerhalb eines Devices erstellt werden. In der Abbildung rechts sieht man eine helligkeitsabhängige Lichtsteuerung für mehrere Szenarien erstellt im DOIF-Perl-Modus mit einer Benutzeroberfläche.

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>}

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 voneinander 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 mit einem Ereignistrigger und einer Fallunterscheidung

defmod di_rc_tv DOIF        \
{                           ## Beginn eines Perlblocks ohne Namen\
  if ([remotecontol:"on"])  ## Das Device "remotecontrol" triggert das Modul, wenn im Event "on" vorkommt\
    {fhem_set"tv on"}       ## wird der Befehl "set tv on" ausgeführt\ 
  else                      ## sonst\ 
    {fhem_set"tv off"}      ## wenn im Event "on" nicht kommt, wird "set tv off" ausgeführt\
}                           ## Ende des Perlblocks

Beispiel

Definition von zwei benannten DOIF-Perl-Blöcken mit Zeittriggern

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]) ## zu definierten Zeitpunkten wird der Block getriggert\
   {fhem_set"radio on"}     ## der Befehl "set radio on" wird ausgeführt\ 
}                           ## 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]) ## zu definierten Zeitpunkten wird der Block getriggert\ 
    {fhem_set"radio off"}   ## der Befehl "set radio off" wird ausgeführt\    
}                           ## Ende des Perlblocks namens radio_off

Aufbau einer Template-Definition

Info green.pngnützliche Links DOIF-Templates
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 bei der Definition intern gegen eine Perlfunktion ersetzt, die einen Rückgabewert bei ihrer Ausführung liefert.

Eventtrigger mit Inhalt

Inhalt des Internals STATE

[<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("push lamp on")}}

Inhalt eines Readings

[<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("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("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]) {fhem_set("lamp on")} else {fhem_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

Zeittrigger, deren Zeitangabe in einem Reading gespeichert wird

[[<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]]) {fhem_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]]) {fhem_set("lamp on")} else {fhem_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, sinnvolle Angaben sind: 02, 03, 04, 05, 06, 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, 3, 4, 6, 8, 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

Trigger unterbinden

Soll eine Ereignis-/Zeitangabe nicht triggern, so lässt sich die Triggerung durch ein vorangestelltes Fragezeichen unterbinden.

[?<Triggerangabe>] <Triggerangabe> kann eine Ereignis- oder Zeitangabe sein
defmod di_lamp DOIF \
{                                                     # hier triggert nur [darkness:state], dagegen wird das Zeitintervall nur abgefragt, \
  if ([darkness:state] eq "on" and [?15:00-22:00]) {  # es gibt keinen Trigger um 15:00 Uhr bzw. um 22:00 Uhr \
    fhem_set("lamp on") \       
  } else { \
    fhem_set("lamp off") \
  } \
}

Aggregationsfunktionen

Info green.pngAggregationsfunktion
[<function>:<format>:"<regex device>:<regex event>":<reading>:<condition>,<default>]

<function>: #, @, #sum, #max, #min, #average, #median, @max, @min
# Anzahl der betroffenen Devices, @ kommagetrennte Liste Devices
#sum Summe, #max höchster Wert, #min niedrigster Wert, #average Durchschnitt, #median Medianwert
@max Device des höchsten Wertes, @min Device des niedrigsten Wertes

<format>: d<number>, a, s(<splittoken>)
d<number> zum Runden des Wertes mit Nachkommastellen, a für Aliasnamen bei Devicelisten, s(<splittoken>) <splittoken> sind Trennzeichen in der Device-Liste

"<regex Device>:<regex Event>" spezifiziert sowohl die betroffenen Devices, als auch den Ereignistrigger, die Syntax entspricht der DOIF-Syntax für Ereignistrigger.

<reading> konkretes Reading, welches überprüft werden soll
alternativ eine Regex in Anführungszeichen
"<regex reading>" Regex für Readings, die überprüft werden sollen

<condition> Aggregationsbedingung
wird <condition> in Anführungszeichen gesetzt, so werden Reading mit eq mit dem angegebenen Wert verglichen. Sonst kann ein Perlausdruck angegeben werden. Folgende Variablen sind vorbelegt und können im Perlausdruck benutzt werden:

$_ Inhalt des Readings
$number Nach Zahl gefilteres Reading
$name Name des Devices
$reading Name des Readings
$TYPE Devices-Typ
$STATE Status des Devices (nicht das Reading state)
$room Raum des Devices
$group Gruppe des Devices

<default> Default-Wert, falls kein Device gefunden wird, entspricht der Syntax des Default-Wertes bei Readingangaben

<format>, <reading>, <condition>, <default> sind optional

Mit Hilfe einer Aggregationsfunktion können mehrere Readings im System ausgewertet werden, die einem bestimmten Kriterium entsprechen. Die Filterkriterien können mit Hilfe von Regex-Ausdrücken für Devices, Events und Readings angegeben werden. Die Aggregationsangabe wirkt triggernd, soll sie nicht triggern, so muss ein Fragezeichen vorangestellt werden, siehe Trigger unterbinden

[<function>:<format>:"<regex device>:<regex event>":<reading>:<condition>,<default>]

Beispiele

Offene Fenster anzeigen. Wenn ein Device beginnend mit "window" im Namen ein Event sendet, dann wird bei allen Devices im System, deren Name mit "window" beginnt, status auf "open" geprüft. Diese werden im Status von di_windows angezeigt. Wird kein Device mit status "open" gefunden, so wird der Default-Wert "keine" übernommen.

defmod di_windows DOIF init {set_State([@:"^window":state:"open","keine"])}

Durchschnittstemperatur aller Temperatursensoren in der Gruppe "livingroom" gerundet auf 2 Nachkommastellen. Wenn ein Event von irgendeinem Device beginnend mit "temperature" auftritt, dann werden alle Devices im System durchsucht, die ein Reading namens "temperature" besitzen und sich in der Gruppe "livingroom" befinden.

defmod di_average_temp DOIF init {set_State ([#average:d2:":^temperature":temperature:$group eq "livingroom"])}

Um 18:00 Uhr werden alle Devices im System durchsucht, deren Reading "battery" nicht "ok" ist. Damit die Aggregationsangabe den DOIF-Perl-Block nicht triggert, muss am Anfang der Angabe ein Fragezeichen vorangestellt werden.

defmod di_batterie_not_ok DOIF init {[18:00];;set_State([?@:":battery":battery:$_ ne "ok","alle OK"])}

Um 18:00 Uhr werden alle Devices im System durchsucht, die ein Reading namens "Battery" oder "battery" haben. Bei diesen Readings wird die letzte Aktualisierung geprüft. Wenn sie älter als 24 Stunden ist, dann wird dessen Device in die Liste aufgenommen. Im Status des Moduls befinden sich danach alle Devices, deren Batterie-Reading länger nicht aktualisiert wurde.

defmod di_batteriecheck DOIF init {[18:00];;set_State([?@:"":"^[Bb]attery$":ReadingsAge($name,$reading,0) > 3600*24,"keine"])}

Das Modul wird getriggert wenn ein beliebiges Device ein Event beginnend mit "temperature" sendet, es werden daraufhin alle Devices nach dem Reading "temperature" durchsucht und der maximale/minimale Wert im Reading max/min von di_min_max gespeichert.

defmod di_min_max DOIF\
{set_Reading("max",[#max:":^temperature":temperature])}\
{set_Reading("min",[#min:":^temperature":temperature])}

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.

Beispiel

Heizung soll bei einer Temperatur unter 21 Grad eingeschaltet werden, sonst soll sie ausgeschaltet werden. Das Unterschreiten bzw. Überschreiten der Wunschtemperatur wird über die Definition des Readings cold realisiert. Obwohl der Temperatursensor zyklisch seine Daten sendet, triggert das Reading cold das eigene Modul und führt zum Schalten der Heizung nur wenn sich sein Zustand (on/off) ändert.

defmod di_Threshold DOIF {                       \
if ([$SELF:cold] eq "on") {  # wenn es kalt ist\
  fhem_set("heating on");;   # Heizung einschalten\
  set_State ("heating");;    # Status auf 'heating' setzen\
} else {                     # sonst\
  fhem_set("heating off");;  # Heizung ausschalten\
  set_State ("not heating");;# Status auf 'not heating' setzen\
} \
}
attr di_Threshold DOIF_Readings cold: ([room:temperature] < 21 ? "on" : "off")


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>. Variablen aus dem Namensraum main werden wie folgt angegeben: $::<Variablenname>-

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, OldReadingsAge, 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>,<Event>); mit <Event>: 0 ohne Eventgenerierung, 1 mit Eventgenerierung, <Event> 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(<Event>); mit <Event>: 0 ohne Eventgenerierung, 1 mit Eventgenerierung

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 Perl-Modus

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

FOR-Schleife

Mit Hilfe der FOR-Schleife kann ein Template in einer Schleife mehrfach aufgerufen werden. Damit können mit einer Parameterliste gleiche Abläufe für verschiedene Szenarien erstellt werden.

FOR (<Array>,<Template>($_$1,$_$2,...)) mit $_$1 erstes Element des Arrays, $_$2 zweites Element des Arrays usw. der jeweiligen Zeile

Beispiel

Lichtsteuerung für mehrere Szenarien

defmod di_light DOIF subs  {\
## In einem Array namens $_sc wird pro Szenario eine Definitionszeile mit Parametern erstellt \
##             Zeitspanne                          Einschaltbefehl              Ausschaltbefehl\
push (@{$_sc},["[06:25-08:00|8] or [18:00-23:30]", "Lampekueche,Lampeflur on", "Lampekueche,Lampeflur off"]);;\
push (@{$_sc},["[06:25-08:00|8] or [18:00-22:10]", "schrank2 on",              "schrank2 off"]);;\
## Das Array kann um weitere Szenarien mit Hilfe des push-Befehls erweitert werden \
} \
## Template zur Steuerung des Lichtes für ein Szenario\
DEF TPL_light ( \
{                   ## Perl-Block \
  if ($1) {         ## In der Zeitspanne \
    fhem_set("$2")  ## Licht einschalten \
  } else { \
    fhem_set("$3")  ## Licht ausschalten \
  } \
}                   ## Ende des Perl-Blocks \
)                   ## Ende von TPL_light \
\
## Mit Hilfe der FOR-Schleife wird das Array $_sc durchlaufen, für jede Szene (Zeile des Arrays) wird das oben definierte Template mit den Parametern der Szene: Zeitspanne $_$1, Einschaltbefehl $_$2 und Ausschaltbefehl $_$2 aufgerufen \
\
FOR (@{$_sc},TPL_light($_$1,$_$2,$_$3)) \
\
## Die FOR-Schleife ruft auf: \
## TPL_light("[06:25-08:00|8] or [18:00-23:30]", "Lampekueche,Lampeflur on", "Lampekueche,Lampeflur off") \
## TPL_light("[06:25-08:00|8] or [18:00-22:10]", "schrank2 on",              "schrank2 off")

Bei dieser Vorgehensweise können weitere Szenarien durch Hinzufügen einer Array-Zeile erstellt werden, ohne den restlichen Code anpassen zu müssen. Desweitern können die Elemente des Arrays für Visualisierungszwecke über das uiTable-Attribut genutzt werden, siehe: Helligkeitsabhängige Lichtsteuerung mit Web-Oberfläche

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\
  }\
}

Weitere Anwendungsbeispiele im DOIF-Perl-Modus

Lichtsteuerung für mehrere Szenarien

Steuerung von Raumthermostaten für mehrere Räume

Beschattungssteuerung für mehrere Szenarien

Abfallentsorgung

Heiztherme

Anwesenheitsstatus