neue Features: ereignisgesteuertes Perl - DOIF-Perl

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

Vorheriges Thema - Nächstes Thema

PatrickR

Hi!

Zitat von: Damian am 04 November 2018, 09:09:13
https://forum.fhem.de/index.php/topic,84969.msg838506.html#msg838506
Wie gesagt, um Tricks ging es mir nicht. Habe das gerade mal zum Anlass genommen, zu schauen, ob man es mit anonymen arrayrefs schöner hinbekommt, was leider scheinbar an der Ausführung mit eval gescheitert ist. Besteht die Möglichkeit, eine variable Anzahl an Parametern in set_Exec() zuzulassen?

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, 12:52:58
Hi!
Wie gesagt, um Tricks ging es mir nicht. Habe das gerade mal zum Anlass genommen, zu schauen, ob man es mit anonymen arrayrefs schöner hinbekommt, was leider scheinbar an der Ausführung mit eval gescheitert ist. Besteht die Möglichkeit, eine variable Anzahl an Parametern in set_Exec() zuzulassen?

Patrick

Das sind keine Tricks, sondern das, was man in jeder Programmiersprache machen würde: ein Pointer auf eine Struktur, hier also ein Array. Eine variable Anzahl ist natürlich in Perl genauso möglich, es zählen die, die du übergibst:

set_Exec("Timer",60,'test(1,2,3,...)')

aber das willst du wahrscheinlich nicht.

Weniger elegant, aber genauso machbar, sind Arrays von Instanzvariablen, die braucht man nicht zu übergeben, da sie global pro DOIF-Device definiert sind.

Beispiel:

defmod di_test4 DOIF init{ @{$_myarray}=("a","b","c")}\
subs{\
sub test {\
set_Reading("erster",@{$_myarray}[0]);;\
set_Reading("zweiter",@{$_myarray}[1]);;\
set_Reading("dritter",@{$_myarray}[2]);;\
}\
}\
{[FS];;set_Exec("Timer",1,'test')}\




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

PatrickR

Hi!

Ich fürchte, wir haben zu einigen Punkten grundsätzlich verschiedene Ansichten :/

Zitat von: Damian am 04 November 2018, 13:22:31
Das sind keine Tricks, sondern das, was man in jeder Programmiersprache machen würde: ein Pointer auf eine Struktur, hier also ein Array.
Trick ist vielleicht nicht das richtige Wort, eher Workaround. Das würde man - exotische Fälle ausgenommen - exakt dann tun, wenn man um künstliche Beschränkungen herumarbeiten muss, die man nicht beeinflussen kann. Ansonsten würde man eher eine Sub mit exakt den gewünschten Argumenten aufrufen, ohne vorher ein Päckchen zu schnüren, das es im nächsten Schritt zu verschicken gilt.

Zitat von: Damian am 04 November 2018, 13:22:31
Eine variable Anzahl ist natürlich in Perl genauso möglich, es zählen die, die du übergibst:
set_Exec("Timer",60,'test(1,2,3,...)')

aber das willst du wahrscheinlich nicht.

Das Problem bei diesem Ansatz ist, dass er nicht nur unschön sondern auch fehlerträchtig ist, insbesondere wenn man keine Literale sondern Variablen übergeben möchte. Dann muss man etwas mehr Aufwand treiben, um den Callback EVAL-tauglich zu machen:

set_Exec("CALLBACK__$DEVICE__$reading", 6, "hmip_callback('$DEVICE', '$reading')");

Ich bin mir natürlich bewusst, dass man dies auf unzählige andere Implementierungsdetails in- und teilweise auch außerhalb von DOIF zutrifft.

Vielleicht denkst Du nochmal über die dynamische Anzahl von Argumenten in set_Exec() nach.

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, 17:34:49

Vielleicht denkst Du nochmal über die dynamische Anzahl von Argumenten in set_Exec() nach.

Patrick

Mal schauen, ob es sich elegant lösen lässt - übrigens InternalTimer kann es auch nicht.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#139
Hier mal ein Beispiel für Fenster-Öffnen-Meldung beim hohen Feuchtigkeitsanstieg (über 10%) nach dem Duschen.

defmod di_fenster_bad DOIF {if ([$SELF:increase] > 10) {fhem_set("Echo_Bad speak Bitte Fenster öffnen")}}
attr di_fenster_bad DOIF_Readings increase:([TH_Bad_HM:humidity]-OldReadingsVal("TH_Bad_HM","humidity",0))/[TH_Bad_HM:humidity]*100


Echo_Bad ich ein Echo-Device im Bad.

Beim HM-Wandthermostat muss das Attribut oldreadings humidity gesetzt werden, damit OldReadingsVal einen Wert liefert.

Jetzt muss sich noch die Definition in der Praxis bewähren :)

Edit: Bewährt! Ansage kommt rechtzeitig kurz nach dem Duschen. Hier noch eine alternative Version:

defmod di_fenster_bad DOIF {if ([$SELF:increase] > 10) {fhem_set("Echo_Bad speak Bitte Fenster öffnen");;set_State("Ansage")}}
attr di_fenster_bad DOIF_Readings increase:([TH_Bad_HM:humidity]/OldReadingsVal("TH_Bad_HM","humidity",50)-1)*100



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

Damian

#140
Zitat von: Damian am 11 November 2018, 21:39:38
defmod di_fenster_bad DOIF {if ([$SELF:increase] > 10) {fhem_set("Echo_Bad speak Bitte Fenster öffnen");;set_State("Ansage")}}
attr di_fenster_bad DOIF_Readings increase:([TH_Bad_HM:humidity]/OldReadingsVal("TH_Bad_HM","humidity",50)-1)*100


Nach dem Öffnen des Fenster kann die Bude im Winter ganz schön auskühlen, wenn man vergisst das Fenster zu schließen.

Mit Fenster-Schließen-Meldung:

defmod di_fenster_bad DOIF {if ([$SELF:increase] and [?BadFenster] eq "closed") {fhem_set("Echo_Bad speak Bitte Fenster öffnen");;set_State("Ansage Öffnen")}}\
{if ([BadFenster:state] eq "open"){set_Reading ("temp",ReadingsVal("TH_Bad_HM","measured-temp",0));;set_State("Fenster geöffnet")}}\
{if ([$SELF:cold] and [?BadFenster] eq "open"){fhem_set("Echo_Bad speak Bitte Fenster schließen");;set_State("Ansage Schließen")}}

attr di_fenster_bad DOIF_Readings increase:([TH_Bad_HM:humidity]/OldReadingsVal("TH_Bad_HM","humidity",50)-1)*100 > 10, cold:([$SELF:temp]-[TH_Bad_HM:measured-temp])>0.5


Das bedeutet, wenn nach dem Öffnen des Fensters die Temperatur um 0,5 Grad fällt, dann Ansage.

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

Damian

#141
Neue Version eingecheckt. Man kann jetzt zwecks Übersichtlichkeit DOIF_Readings mit Zeilenumbrüchen an beliebiger Stelle definieren, z. B. pro Reading eine Zeile:

attr di_fenster_bad DOIF_Readings
increase: [?BadFenster] eq "closed" and ([TH_Bad_HM:humidity]/OldReadingsVal("TH_Bad_HM","humidity",50)-1)*100 > 10,
cold:[?BadFenster] eq "open" and ([$SELF:temp]-[TH_Bad_HM:measured-temp])>0.5


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

Damian

#142
Wenn jemand nicht weiß wohin mit der Rechenpower.

Feuerfackeln im Garten

defmod di_garten DOIF {[+(rand(10)/10)];;fhem_set"Garten pct ".(10+rand(90))}

Funktioniert ganz gut mit Osram Smart+ LED ZigBee Außen-/Gartenleuchte über HUE-Bridge. Mit anderen Devices z. B. HM wegen credits nicht zu empfehlen. Die Farbe muss man sich selbst voreinstellen. Orange ist ganz nett für Feuer.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

sk001

Hallo!
Ich versuche mit den Perl-Funktionen im DOIF einen Lichtwecker zu realisieren. Hierzu soll ein dummy (du_Weckzeit) verwendet werden, über das eine Weckzeit in der Benutzeroberfläche festgelegt wird. Das DOIF ist wie folgt formuliert:

init {$_value=0;
     fhem"set deConz_HUEDevice1 pct 0";} main {
if ( [[du_Weckzeit]-{weckerZeitbereich(du_Weckzeit,600)},+00:01 ] and [?du_Wecker_aktiv] eq "an" ) {
   fhem"set deConz_HUEDevice1 pct $_value";
   $_value+=5;
}
else {
  $_value=0;
}
}


Die Funktion "weckerZeitbereich" ist in der 99_myUtils.pm definiert. Ihr werden als Parameter die Startzeit aus dem du_Weckzeit und die Sekunden für die Berechnung des Zeitintervalls übergeben:

sub weckerZeitbereich($$) {
  my ($beginn,$intervall)= @_;
  my $ende = ( str2time(localtime($beginn)+$intervall));

  return (strftime ("%H:%M", localtime ($ende) ) );
}


Das DOIF gibt mir leider in allen bisher probierten Konstellationen einen Fehler für den timer_02_c02 wie folgt aus: error: the function "weckerZeitbereich(du_Weckzeit,600)" must return a timespec and not Bareword "du_Weckzeit" not allowed while "strict subs" in use at (eval 61697) line 1.

Trotz aller Versuche kann ich das Problem leider nicht lösen. Was ist bei der Definition eines Zeitintervalls, wie in der Referenz beschrieben, zu beachten? Mein Ziel ist also so etwas unter Verwendung des Dummys zu erzeugen:

define di_rand_lamp DOIF ([{sunset()}-[end:state],+(rand(600)+900)|Sa So])(set lamp on-for-timer 300)

Danke für die Hilfe!

Damian

Du brauchst die Funktion nicht. Du kannst direkt angeben:

if ( [[du_Weckzeit]-([du_Weckzeit]+600),+00:01 ] and [?du_Wecker_aktiv] eq "an" ) {
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

sk001

Hallo Damian!
Dann lag es wohl an der Klammersetzung... ::). Es funktioniert jetzt!
Danke für Deine Hilfe! Klasse, was Dein Modul alles kann...!

obi

Hallo,

ich habe in mehreren DOIFs Subroutinen definiert welche den gleichen Namen haben. Dies führt zu Problemen, da diese global in allen DOIF zur Verfügung stehen. Gibt es eine Möglichkeit Subs zu definieren nur für das eigene DOIF?
Ich konnte hierzu nichts in der Commandref/Wiki finden.

Damian

#147
Zitat von: obi am 03 Februar 2019, 11:21:18
Hallo,

ich habe in mehreren DOIFs Subroutinen definiert welche den gleichen Namen haben. Dies führt zu Problemen, da diese global in allen DOIF zur Verfügung stehen. Gibt es eine Möglichkeit Subs zu definieren nur für das eigene DOIF?
Ich konnte hierzu nichts in der Commandref/Wiki finden.

Die Subroutinen sind nur gegenüber dem Rest von FHEM (alles außerhalb von DOIFs) gekapselt. Alle DOIF-sub-Definitionen befinden sich im gleichen Namensraum DOIF.

Gleiche Subroutinen innerhalb der DOIF-Definitionen lassen sich durch Angabe von $SELF einfach eindeutig machen.

Bsp.:

define test1 DOIF
subs { sub $SELF_sub1 {...}} ## Definition von sub1
{if ([mytrigger]) { $SELF_sub1()}} ## Aufruf von sub1


Edit:

alternativ kann man ein eigenes Package definieren:

define test1 DOIF
subs {package $SELF; sub sub1 {DOIF::fhem_set"bla on"}} ## Definition von sub1
{if ([mytrigger]) { $SELF::sub1()}} ## Aufruf von sub1


oder

define test1 DOIF
subs {package $SELF; sub sub1 {DOIF::fhem_set"bla on"}} ## Definition von sub1
{package $SELF; if ([mytrigger]) {sub1()}} ## Aufruf von sub1



dabei ist zu beachten, dass DOIF-Perl spezifische Funktionen des Namensraums DOIF  im eigenen Package mit vorangestelltem DOIF:: angegeben werden müssen, FHEM-subs müssen ja ohnehin mit vorangestelltem :: angegeben werden.

Statt $SELF kann man natürlich einen beliebigen eigenen Namen angeben, dann muss man darauf achten, dass man in jedem DOIF einen anderen package-Namen angibt. Bei $SELF ist der Name bereits eindeutig, weil es der DOIF-Device-Name ist.

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

obi

Danke Damian für die schnelle Antwort, funktioniert so super :)

Damian

Zitat von: obi am 04 Februar 2019, 20:19:11
Danke Damian für die schnelle Antwort, funktioniert so super :)

Für welche Lösung hast du dich denn entschieden?
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF