neue Features: ereignisgesteuertes Perl - DOIF-Perl

Begonnen von Damian, 25 Februar 2018, 21:29:16

Vorheriges Thema - Nächstes Thema

Ellert

Zitat von: Damian am 10 Oktober 2018, 18:34:04
Ich habe mal folgende Version gebaut:

State wird gar nicht mehr vom Modul gesetzt. Es gibt nur noch das Reading mode (enabled, disabled). Z. Zt. ohne weitere Readings nach der Neudefinition.

gerade noch getestet:

[$SELF:state] liefert leeren String

[$SELF] liefert drei Fragezeichen.

Alternativ könnte man state bei defmod ohne Trigger auf initialize setzen.

Des Weiteren habe ich in der Konsequenz die Attribute state, initialized, startup im Perl-Modus entfernt. Es gibt ja jetzt den init-Block. Ich hoffe diese Attribute benutzt noch keiner im Perl-Modus.

Neue Version 0.3 im Anhang zum Antesten.

Edit: Das disable-Attribut wird jetzt auch berücksichtigt -> mode deactivated wie bisher aber ohne Statusänderung
Edit: Jetzt werden Readings sofort angelegt, wenn man das Attribut DOIF_Readings definiert

Ich denke immer noch das Reading state sollte nicht gelöscht werden, dann haben Veränderungen der Definition keinen ungewollten Einfluss auf state.
Wenn der Benutzer es möchte, könnte er das Reading state im init Block löschen, mit https://wiki.fhem.de/wiki/DevelopmentModuleAPI#readingsDelete

Mir ist noch aufgefallen, dass die Funktion Log und Log3, die im Package DOIF definiert wird, beim Laden der 99_myUtils während des Hochfahrens noch nicht zur Verfügung steht und vorab deklariert werden muss. Aber z.B. get_State und InternalVal funktioniert ohne Vorabdeklaration.

99_myUtils
package DOIF;
sub Log;

# Enter you functions below _this_ line.
sub testFn {
  my $s = get_State();
  my $i = InternalVal("initBlock","MODEL","---");
  Log 1, "initBlock testFN $s Mode $i";
  Log3 "initBlock",2, "initBlock testFN $s Mode $i";
}

Testdefinition
defmod initBlock DOIF init {\
  Log 1, "$SELF:".ReadingsVal("$SELF","state","none");;\
  testFn();;\
}



Damian

#121
ja,  Readings, die das Modul nicht setzt, werden ja vom Modul nicht gelöscht - das würde dann tatsächlich auch für state zutreffen.  Das kann ich noch ausbauen.

Keine Ahnung warum man Log vordeklarieren muss und get_State nicht. Das Problem könnte man vermutlich mit der Angabe ::Log aus dem main-Bereich umgehen. Das muss ich mir noch genauer anschauen.

Edit: Es liegt wohl daran, dass Log bzw. Log3 ohne Klammern angegeben wurden. Ich weiß nicht warum sich das eingebürgert hat, Funktionen insb. mit mehreren Parametern ohne Klammern anzugeben. Also immer mit Klammern angeben:

package DOIF;

# Enter you functions below _this_ line.
sub testFn {
  my $s = get_State();
  my $i = InternalVal("initBlock","MODEL","---");
  Log (1, "initBlock testFN $s Mode $i");
  Log3 ("initBlock",2, "initBlock testFN $s Mode $i");
}
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

neue Version eingecheckt: Perl-Modus: state wird vom Modul nicht angepackt (auch nicht gelöscht), Attribute state, initialize, startup wurden entfernt, set initialize wurde ebenfalls entfernt
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Ellert

set_State("abc",0) erzeugt ein Event und ignoriert die Triggerangabe. Die Commandref ist dem Programmcode voraus ;)

Damian

Zitat von: Ellert am 14 Oktober 2018, 17:58:04
set_State("abc",0) erzeugt ein Event und ignoriert die Triggerangabe. Die Commandref ist dem Programmcode voraus ;)

Jetzt nicht mehr, korrigiert und eingescheckt ;)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#125
Hier mal Beispiele wie man wait mit do always bzw. mit do resetwait im Perlmodus realisieren kann:

resetwait

DOIF (["EVENT"]) (set ....)
attr do resetwait
attr wait 60


entspricht im Perl-Modus:

DOIF {if (["EVENT"]) {set_Exec("Timer",60,'fhem_set("...")')}}

alternativ:

DOIF {["EVENT"];set_Exec("Timer",60,'fhem_set("...")')}

always

DOIF (["EVENT"]) (set ....)
attr do always
attr wait 60


entspricht im Perl-Modus:

DOIF {if (["EVENT"]) {if (!get_Exec("Timer")) {set_Exec("Timer",60,'fhem_set("...")')}}}

alternativ:

DOIF {["EVENT"]; set_Exec("Timer",60,'fhem_set("...")') if (!get_Exec("Timer"))}

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

kalleknx

Hi Damian,

ich habe eben von set_Timer/del_Timer auf set_Exec bzw. del_Exec gewechselt.

Frage: wie kann ich subroutinen aus der 99_myUtils.pm innerhalb der Subs {} des DOIFs aufrufen?
Bekomme momentan folgenden Fehler:

error - in MowerHome("Mower"): Undefined subroutine &DOIF::pushMsgDebug called at (eval 6789) line 5.


THX

Damian

Zitat von: kalleknx am 16 Oktober 2018, 20:19:10
Hi Damian,

ich habe eben von set_Timer/del_Timer auf set_Exec bzw. del_Exec gewechselt.

Frage: wie kann ich subroutinen aus der 99_myUtils.pm innerhalb der Subs {} des DOIFs aufrufen?
Bekomme momentan folgenden Fehler:

error - in MowerHome("Mower"): Undefined subroutine &DOIF::pushMsgDebug called at (eval 6789) line 5.


THX

so, wie es in der Commandref beschrieben ist, indem man beim Aufruf :: vor die Routine angibt, hier also ::pushMsgDebug(...
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Da im Perl-Modus im Gegensatz zum FHEM-Modus bewusst keine automatische Statusabfrage (Zustandsauswertung) erfolgt , muss der Anwender selbst darauf achten insb. bei zyklisch sendenden Sensoren, das Wiederholen einer Anweisung zu unterdrücken. In diesem Zusammenhang ist die Verwendung des Attributes DOIF_Readings, welches insb. für diesen Zweck konzipiert wurde, besonders interessant. Deswegen habe ich die Commandref zu DOIF-Readings angepasst:

ZitatErzeugen berechneter Readings

Mit Hilfe des Attributes DOIF_Readings können eigene Readings innerhalb des DOIF definiert werden, auf die man im selben DOIF-Moduls zugreifen kann. Die Nutzung ist insbesondere dann sinnvoll, wenn zyklisch sendende Sensoren, im Perl-Modus oder mit dem Attribut do always, abgefragt werden. DOIF_Readings-Berechnungen funktionieren ressourcenschonend ohne Erzeugung FHEM-Events nach außen. Änderungen dieser Readings triggern allerdings das eigene DOIF-Modul, allerdings nur, wenn sich deren Inhalt ändert.

Syntax

attr <DOIF-Modul> DOIF_Readings <readingname1>:<definiton>, <readingname2>:<definition>,...

<definition>: Beliebiger Perlausdruck ergänzt um DOIF-Syntax in eckigen Klammern. Angaben in eckigen Klammern wirken triggernd und aktualisieren das definierte Reading.

Beispiel

Perl-Modus:
define heating DOIF {if ([switch] eq "on" and [$SELF:frost]) {fhem_set"heating on"} else {fhem_set"heating off"}}
attr heating DOIF_Readings frost:([outdoor:temperature] < 0)


Das Reading frost triggert nur dann die definierte Abfrage, wenn sich sein Zustand ändert. Dadurch wird sichergestellt, dass ein wiederholtes Schalten der Heizung vermieden wird, obwohl der Sensor outdoor zyklisch sendet.

Beispiel: Push-Mitteilung über die durchschnittliche Temperatur aller Zimmer

define di_temp DOIF ([$SELF:temperature]>20) (push "Die Durchschnittstemperatur ist höher als 20 Grad, sie beträgt [$SELF:temperature]")

attr di_temp DOIF_Readings temperature:[#average:d2:":temperature":temperature]


Hierbei wird der aufwändig berechnete Durchschnittswert nur einmal berechnet, statt zwei mal, wenn man die Aggregationsfunktion direkt in der Bedingung und im Ausführungsteil angeben würde.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#129
Hier mal ein Beispiel wie man etwas übersichtlicher Temperaturvorgaben definieren kann:

Bisher im FHEM-Modus:

DOIF ([05:00|8] or [05:30|7])
  (set TH_Keller_HM desired-temp 20,
   set TH_Kueche desired 21,
   set TH_Bad_HM desired-temp 21,
   set TH_DG_HM desired-temp 21,
   set TH_WZ_HM desired-temp 21,
   ##set TH_Kz_o_HM desired-temp 21.5,
   set TH_Kz_w_HM desired-temp 20)
DOELSEIF ([08:00|8] or [09:00|7])
  (set TH_Keller_HM desired-temp 19.5,
   set TH_Kueche desired 17,
   set TH_Kz_w_HM desired-temp 17,
   set TH_Bad_HM desired-temp 17,
   set TH_DG_HM desired-temp 20)
DOELSEIF ([12:00])
  (set TH_Kueche desired 19.5,
   set TH_WZ_HM desired-temp 21,
   set TH_Kz_w_HM desired-temp 21,
   set TH_DG_HM desired 21)
DOELSEIF ([17:00])
  (set TH_Bad_HM desired-temp 17)
DOELSEIF ([20:00])
  (set TH_Kueche desired 17,
   set TH_WZ_HM desired-temp 20,
   set TH_Kz_w_HM desired-temp 20,
   set TH_Bad_HM desired-temp 17,
   set TH_DG_HM desired-temp 20)
DOELSEIF ([23:00])
   (set TH_Keller_HM desired-temp 19.5)


Tabellarisch im Perl-Modus:

DOIF
## Raum       Befehl          05:00 08:00 12:00 17:00 20:00 23:00
init {$_temps="
TH_Keller_HM, desired-temp,   20,   19.5, ,     ,     ,     19.5
TH_Kueche,    desired,        21,   17,   19.5, ,     17,
TH_Bad_HM,    desired-temp,   21,   17,   ,     17,   17,
TH_DG_HM,     desired-temp,   21,   20,   21,   ,     20,
TH_WZ_HM,     desired-temp,   21,   17,   21,   ,     20,
TH_Kz_w_HM,   desired-temp,   20,   17,   21,   ,     20,";
   @{$_line}=split ('\n',$_temps);
}

{temp_set(0) if ([05:00|8] or [05:30|7])}
{temp_set(1) if ([08:00|8] or [09:00|7])}
{temp_set(2) if ([12:00])}
{temp_set(3) if ([17:00])}
{temp_set(4) if ([20:00])}
{temp_set(5) if ([23:00])}

subs  {
  sub temp_set {
    my ($l)=@_;
    foreach (@{$_line}) {
      my ($raum,$Befehl,@Phase)=split (', *',$_);
      fhem_set("$raum $Befehl $Phase[$l]") if ($Phase[$l]);
    }
  } 
}


Edit: Code angepasst
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Ich hatte mal ein DOIF definiert, welches für jedes Device, welches eine Batteriemeldung liefert, ein Reading erstellt mit dem Status ok oder low.

Wenn man einen häufigen Statuswechsel zwischen ok/low vermeiden wollte, hätte man bei einem Device mit einem wait-Timer arbeiten können, da dieses DOIF aber viele Devices beobachtete, wäre pro Device ein eigener wait-Timer nötig.

So etwas geht nun im Perl-Modus, da hier beliebig viele wait-Timer (pro Device eins) gleichzeitig laufen können:

Beispiel:

defmod di_bat DOIF {if ([":battery: low"]) {\
  if (get_Reading ('B_$DEVICE') ne "low") {\
    if (get_Exec("Timer_$DEVICE_low")==0) {\
     set_Exec("Timer_$DEVICE_low",28800,"set_Reading ('B_$DEVICE','low')");;\
    }\
  } elsif (get_Exec("Timer_$DEVICE_ok")) {\
    del_Exec("Timer_$DEVICE_ok")\
  }\
}\
}\
{if ([":battery: ok"]) {\
  if (get_Reading ('B_$DEVICE') ne "ok") {\
    if (get_Exec("Timer_$DEVICE_ok")==0) {\
     set_Exec("Timer_$DEVICE_ok",28800,"set_Reading ('B_$DEVICE','ok')");;\
    }\
  } elsif (get_Exec("Timer_$DEVICE_low")) {\
    del_Exec("Timer_$DEVICE_low")\
  }\
}\
}\


Es entsteht mit dem obigen Code im DOIF-Device ebenfalls pro Device ein Reading namens B_<Device-Name>, allerdings erst dann, wenn jeweils 8 Stunden lang der jeweilige Batterie-Status (low/ok) stabil bleibt.

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

Neue DOIF-Version eingecheckt.

Wollte man im init-Block eine Funktion aus dem subs-block aufrufen, so musste bisher der subs-Block zuerst definiert werden. Nun ist die Reihenfolge egal (der init-Block wird jetzt als letzter ausgewertet), es funktioniert dann auch so etwas:

DOIF
init {test()}
subs {sub test {set_State("blabla")}}



Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

PatrickR

#133
Mahlzeit!

Habe nun gezwungenermaßen etwas mit set_Exec experimentiert und es gefällt mir deutlich besser als die set_timer-Veriante.

Nun eine Frage: Wie kann ich der aufgerufenen Sub denn mehr als einen Parameter übergeben? (ohne Tricks wie split() etc.)

Patrick
lepresenced - Tracking von Bluetooth-LE-Tags (Gigaset G-Tag) mittels PRESENCE

"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning." - Rich Cook

Damian

Zitat von: PatrickR am 04 November 2018, 00:02:03
Mahlzeit!

Habe nun gezwungenermaßen etwas mit set_Exec experimentiert und es gefällt mir deutlich besser als die set_timer-Veriante.

Nun eine Frage: Wie kann ich der aufgerufenen Sub denn mehr als einen Parameter übergeben? (ohne Tricks wie split() etc.)

Patrick

https://forum.fhem.de/index.php/topic,84969.msg838506.html#msg838506
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF