Hallo,
ich sammle gerade Ideen für ein neues Modul. Es soll ereignisgesteuertes Perl für Automatisationsaufgaben werden.
Features:
- beliebige Perl-Blöcke
- Ereignis-Angaben in eckigen Klammern [:] triggern den jeweiligen Perl-Block, dieser wird ausgeführt. Diese Angaben stellen Funktionsaufrufe dar, sie können überall dort im Perl-Block angegeben werden, wo eine Perlfunktion vorkommen kann.
- Variablen außerhalb der Perlblöcke sind global innerhalb des Moduls und bleiben auch nach der Ausführung eines Blocks erhalten
Definitionsbereich DEF:
my $old_warm="";
my $mode="enable";
{# Ereignisgesteuerte Heizungssteuerung, dieser Block wird ausgeführt wenn das Reading "temperature" vom Device "sensor" ein Event erzeugt
my $warm;
return if ($mode eq "disable"); # nichst tun wenn Modus disable
$warm=([sensor:temperature] > 20 ? "on":"off");
if ($warm != $old_warm) { # wenn sich der Zustand warm/kalt ändert, dann soll die Heizung ein- bzw. ausgeschaltet werden
if ($warm eq "off") {fhem"set heating on"} else {fhem"set heating on"};
readingsSingleUpdate($hash, "state",$warm,1) # der Status wird auf on gesetzt, wenn geheizt wird, sonst auf off
$old_warm=$warm; # der aktuelle Zustand wird in der globalen Variable $old_warm für den nächsten Durchlauf gespeichert
}
}
{ # zeitgesteuert Heizung ein- und ausschalten, dieser Block wird um 05:00 und 09:00 Uhr ausgeführt
if ([05:00]) {$mode="enable"} elsif ([09:00]) {$mode="disable";fhem"set heating off"}; #Modus wird zeitabhängig gesetzt
readingsSingleUpdate ($hash, "mode",$mode,1); # der aktuelle Modus wird im Reading mode gespeichert
}
In diesem Beispiel sind zwei Perlblöcke innerhalb eines Moduls programmiert, die unabhängig von einander ausgeführt werden.
Bei dem neuen Modul muss man sich mehr um Details kümmern, wie z. B. Status setzen, als beim DOIF-Modul, dafür ist man flexibler. Auf der anderen Seite muss man aber nicht gleich ein eigenes Modul programmieren, wenn man etwas systemnäher automatisieren will.
Wäre es nicht besser, wenn die Variablen ausserhalb der Blöcke nur im Device global sind und nicht im Modul? Sonst müsste man für jedes Device neue Variablennamen verwenden.
Zitat von: Ellert am 21 Februar 2018, 10:09:32
Wäre es nicht besser, wenn die Variablen ausserhalb der Blöcke nur im Device global sind und nicht im Modul? Sonst müsste man für jedes Device neue Variablennamen verwenden.
ja, so ist das auch gemeint. Die Variablen sollen pro Instanz global sein. Eigentlich will ich möglichst wenig Einfluss auf die Definition nehmen, damit sich Perl-Programmierer schnell zurechtfinden, daher wird es wohl besser sein, gleich mit hash-Variablen zu hantieren, statt sie intern zu maskieren - sie gelten pro Device-Instanz. Hier am Beispiel also:
$hash->{var}{old_warm}="";
$hash->{var}{mode}="enable";
Wenn eine Variable auch ein Reboot überleben soll, dann kann man sich mit Readings behelfen, z. B.:
$hash->{var}{mode}=[$SELF:mode,""];
oder auch
$hash->{var}{mode}=ReadingsVal($self,"mode","");
Damit wird die hash-Variable mode mit dem Reading mode der Device-Instanz nach dem Hochfahren des System belegt. Wenn das Reading nicht existiert. wird "" genommen. Die Option Defaultwert-Angaben anzugeben liefert bereits die [:] - Syntax (entspricht der heutigen DOIF-Syntax).
Hm, ich verstehe noch nicht den Mehrwert gegenüber at, notify und doif. Oder habe ich da gerade eine Scheuklappe?
Gesendet von iPhone mit Tapatalk
Grüße Jörg
Zitat von: JoWiemann am 21 Februar 2018, 11:07:58
Hm, ich verstehe noch nicht den Mehrwert gegenüber at, notify und doif. Oder habe ich da gerade eine Scheuklappe?
Gesendet von iPhone mit Tapatalk
Grüße Jörg
Hast du schon mal versucht Ereignissteuerung kombiniert mit Zeitsteuerung mit übergreifenden Variablen gekapselt als Objekt zu programmieren? Wenn ja, dann hast du ein FHEM-Modul programmiert. at, notify aber auch DOIF kann das nicht sauber abbilden.
Klingt irgendwie interessant, aber mir will kein wirkliches konkretes Problem dafür einfallen, welches dadurch einfacher gelöst wird? Was war denn der Auslöser für die Idee?
Das Beispiel aus dem ersten Beitrag ist ja, wenn ich mich nicht irre, mit 2 DOIF Zeilen lösbar! Oder?
Mir fehlt irgendwie noch ein Zeiger auf die Klammerblöcke, damit man diese zusätzlich aus anderen Blöcken ansprechen/triggern könnte.
Zitat von: Phill am 21 Februar 2018, 14:25:53
Klingt irgendwie interessant, aber mir will kein wirkliches konkretes Problem dafür einfallen, welches dadurch einfacher gelöst wird? Was war denn der Auslöser für die Idee?
Das Beispiel aus dem ersten Beitrag ist ja, wenn ich mich nicht irre, mit 2 DOIF Zeilen lösbar! Oder?
Mir fehlt irgendwie noch ein Zeiger auf die Klammerblöcke, damit man diese zusätzlich aus anderen Blöcken ansprechen/triggern könnte.
Natürlich wäre das Beispiel mit 2 DOIFs, ja vermutlich sogar auch mit einem lösbar.
Wahrscheinlich sind die meisten einfachen Lampe an/aus - Problemstellungen kürzer und eleganter mit DOIF lösbar, immerhin wird Zeit und Ereignissteuerung innerhalb eines Moduls unterstützt.
Das Problem entsteht, wenn die Anforderungen steigen. Dann kann man versuchen alles in ein DOIF zu packen oder auf mehrere zu verteilen.
Im ersten Fall wird es irgendwann unübersichtlich, da DOIF eine flache Hierarchie benutzt - es dreht sich alles um den Zustand von State.
Im anderen Falle wird ein Problem auf mehrere DOIF-Module funktional verteilt. Wenn man eine Strukturierung vornehmen will, dann kann man die zusammen hängenden DOIF-Module in einen Raum packen, vielleicht auch eine gemeinsame Gruppe definieren, das war es aber auch schon.
Wenn man aber den Gedanken der Kapselung im Sinne von Objektorientierung verfolgt, dann müsste man schon ein FHEM-Modul programmieren.
Der Aufwand dürfte aber für die meisten Anforderung einfach zu hoch sein.
Irgendwo dazwischen sollte das neue Modul seinen Einsatz finden können.
Aufgrund, der vielen Funktionen, die ich für DOIF programmiert habe, könnte ich mir vorstellen relativ schnell einen Prototypen zu bauen - die meisten Funktionen dafür stecken schon in einem DOIF. Danach kann man damit etwas rumspielen und schauen, wie gut/elegant oder schwierig Probleme damit lösbar sind.
Ich denke, viele Anfänger wird es abschrecken, da es Perl ist. Auf der anderen Seite finde ich es reizvoll, Ereignis- und Zeisteuerung in Perl unterzubringen, ich meine damit bewusst nicht: Perl in einem Ereignis- oder Zeit-Modul unterzubringen - das haben wir ja schon ;)
so könnte ein generalisierter Perl-Block aussehen. Dieser Code beinhaltet sowohl die Thermostatsteuerung als auch die zeitabhängige Vorgabe der Temperaturen für alle Zimmer.
{# Ereignisgesteuerte Thermostatsteuerung, dieser Block wird ausgeführt, wenn das Reading "temperature" von irgendeinem Device ein Event erzeugt
my $temp=[":temperature",0];
return if (ReadingsVal($self,"mode","disable") eq "disable");
my $warm=($temp > ReadingsVal($device,"desired",20) ? "on":"off");
if (!defined $hash->{var}{$device}{warm} or $warm != $hash->{var}{$device}{warm}) {
if ($warm eq "off") {
fhem"set switch_$device on"
} else {
fhem"set switch_$device off"
}
$hash->{var}{$device}{warm}=$warm;
}
}
{# in diesem Block werden die Vorgabetemperaturen zeitabhängig gesetzt
if ([05:00] or [14:00]) {
fhem"setreading Kueche desired 20";
fhem"setreading Bad desired 21";
fhem"setreading Wohnzimmer desired 19";
fhem"setreading KinderZimmer1 desired 20";
} elsif ([09:00] or [22:00]) {
fhem"setreading Kueche desired 19";
fhem"setreading Bad desired 20";
fhem"setreading Wohnzimmer desired 18";
fhem"setreading KinderZimmer1 desired 19";
}
}
Wie soll das Modul heissen? PerlMutt, PerlHabour, DOPE(rl) ;)
Zitat von: Ellert am 22 Februar 2018, 16:22:47
Wie soll das Modul heissen? PerlMutt, PerlHabour, DOPE(rl) ;)
Ganz einfach: DOIF
Und das Beste - es läuft schon :)
Der Perl-Modus wird automatisch erkannt.
Das Modul ist voll abwärtskompatibel. Es ist gerade mal 1 % größer.
Definitionen außerhalb der Perlblöcke sind noch nicht eingebaut.
meine ersten Tests sehen so aus:
defmod di_test2 DOIF {if ([FS] eq "on") {fhem"set bla1 on"}}\
{if ([FS] eq "off") {fhem"set bla2 on"}}\
{if ([19:23]) {fhem"set bla3 on"}}\
{if ([19:23]) {fhem"set bla4 on"}}\
{if ([19:24]) {fhem"set bla5 on"}}
Edit: Anhang entfernt
Ich hatte gedacht, es wird ein um die 10kB schlankes Modul, es müssten eigentlich "nur" die Trigger registriert werden. :)
Zitat von: Ellert am 22 Februar 2018, 23:26:17
Ich hatte gedacht, es wird ein um die 10kB schlankes Modul, es müssten eigentlich "nur" die Trigger registriert werden. :)
ja, uiTable sollte nicht fehlen, da ein Status nicht mehr ausreicht, DOIF_Readings sind wichtig, um zyklische Sensoren sauber abzufangen, Timer-Verwaltung, usw. -> 1 % + 80 % (DOIF-Funktionalität) ;)
DIE Killeranwendung dafür habe ich noch nicht gefunden, aber wahrscheinlich wird sich die Benutzung "schleichend" ergeben. Wie die Perlblöcke in {}, die ja jetzt schon gehen (gleibe ich ;)).
Zitat von: Per am 23 Februar 2018, 11:42:29
DIE Killeranwendung dafür habe ich noch nicht gefunden, aber wahrscheinlich wird sich die Benutzung "schleichend" ergeben. Wie die Perlblöcke in {}, die ja jetzt schon gehen (gleibe ich ;)).
An dem einfachen Beispiel sieht man schon etwas, was in einem DOIF so nicht funktionieren würde.
Die Blöcke sind völlig unabhängig von einander. Beim Event von FS kann also Block 1 und Block 2 etwas ausführen. Genauso bei der Zeitangabe, um 19:23 wird sowohl Block 3 und Block 4 ausgeführt. Das Beispiel ist hier sinnfrei, da es nur einen Test darstellt.
Man kann festhalten: DOIF: ein Event -> nur ein Zweig kann ausgeführt werden, DOIF(Perl): ein Event -> beliebig viele Zweige können ausgeführt werden.
DOIF(Perl) kennt keine Zustände, es gilt also immer do always. Weil es einfach gestrickt ist, gibt es keine internen Abhängigkeiten, die man erklären müsste. Es funktioniert eher so, wie ein Programmierer es erwartet hätte, dafür muss man sich selbst drum kümmern Zustände zu verwalten und Readings/Status selbst zu setzen.
Auf der anderen Seite kann man bei DOIF(Perl) auf der Perlebene mit Variablen arbeiten, mit hash->{var} hat man die Möglichkeit pro Instanz beliebig viele Variablen zu nutzen, die nach der Ausführung erhalten bleiben und beim nächsten Event ausgewertet werden können. Beim DOIF muss man sich da mit Readings auf der FHEM-Ebene behelfen. Ein einfaches hochzählen dürfte Faktor 1000 langsamer sein.
So, was noch fehlt ist ein Ersatz für watchdog, sequenz, waittimer.
Beispiel
{ if ([FS:"on"]) {
set_DOIF_timer(300,"Timer 1");
} elsif ([FS:"off"]) {
del_DOIF_timer("Timer 1");
} elsif ([$SELF:"Timer 1"]) {
fhem "set lamp on";
}
}
mit
set_DOIF_timer (<sekunden>, <Timername>, <Trigger optional>)
del_DOIF_timer(<Timername>);
Damit würde man beim Event "on" von FS einen Timer setzen mit dem Namen "Timer 1", wenn zwischendurch ein "off" von FS kommt wird der Timer gelöscht, wenn der Block allerdings durch einen Timer-Trigger geweckt wird, dann wird Lampe eingeschaltet.
Da man beim set_DOIF_timer Events erzeugen kann z. B. set_DOIF_timer (300,"mytimer",1), so kann man systemweit darauf reagieren.
Edit: Syntax überarbeitet
Nächste Gedankenstufe: Namen für Perlblöcke, Aufrufe von Perlblöcken, Übergabeparameter
Damit man sich besser im System zurechtfindet sind Namen besser als Zahlen, daher sollte man Blöcke wie Funktionen benennen können, Syntax
block <name>
{
...
}
Wie kann man einen Block aufrufen und ihm Informationen übergeben?
Da wir hier von Ereignissteuerung sprechen, kann man nicht nur einen Block, sondern gleich mehrere gleichzeitig aufrufen :)
Das realisiert man über Events:
{
#Aufruf
set_DOIF_event ("blabla")
...
}
{
# in diesem Block wird auf das jeweilige Event reagiert
if ([$SELF:"blabla"]) {fhem"set lamp on"}
...
}
mit set_DOIF_event (<Eventinhalt>, <Trigger optional>)
Events innerhalb eines DOIF-Moduls brauchen keinen Event-Trigger, daher kann er weggelassen werden, das ist ressourcenschonender. Wenn man mit anderen kommunizieren möchte (außerhalb des eigenen DOIF-Moduls), dann erzeugt man einfach ein Event mit: set_DOIF_event ("blabla", 1), darauf kann man systemweit reagieren.
Bei den angepinnten Beiträgen im DOIF-Board habe ich schon aufgeräumt - es wird ja Platz benötigt für neue Features :)
Ich habe die Timersyntax konsequenterweise auf Events geändert: siehe https://forum.fhem.de/index.php/topic,84692.msg771569.html#msg771569
Zitat von: Damian am 24 Februar 2018, 09:44:48
Ich habe die Timersyntax konsequenterweise auf Events geändert: siehe https://forum.fhem.de/index.php/topic,84692.msg771569.html#msg771569
neue DOIF-Version siehe hier: https://forum.fhem.de/index.php/topic,84969.0.html
Hi Damian,
ich finde den Ansatz wirklich interessant.
Ich finde gut, dass ich bekannte Perl-Syntax ohne drumherum nutzen kann. Die '[:]'-Schreibweise finde ich auch super. Zusätzlich kann ich jetzt mehrere unabhängige Blöcke in einem DOIF zusammenfassen, das fehlte mir vorher.
Ich werd's weiter beobachten und testen :)
LG
Christian