neue Features: ereignisgesteuertes Perl - DOIF-Perl

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

Vorheriges Thema - Nächstes Thema

Damian

#240
Neue Version wurde eingecheckt.

Ich habe im Perlmodus die Option eingebaut, dass man mehrere init-Blöcke definieren kann. Ein init-Block wird automatisch beim Hochfahren des Systems ausgeführt oder unmittelbar nach der Definition eines DOIF-Devices. Damit lassen sich Templates erstellen, die alle einen eigenen init-Block brauchen.

D.h. wenn ein Block init heißt oder neuerdings mit init_ beginnt, so wird er wie ober beschrieben zusätzlich ausgeführt.

Hier ein Beispiel mit dem neuen Feature, wie man bei einem fortlaufenden Zähler, wie bei Strom, Wasser, Gas eine Tages-, Monats- und Jahresstatistik erstellen kann (die vorhanden Module zu dem Thema haben mir nicht zugesagt).


defmod di_counter DOIF DEF TPL_stat (midnight_$1_$2 { [00:01];;\
  set_Reading("$1_$2_counter",[?$1:$2]);;\
  set_Reading("$1_$2_last_day",get_Reading("$1_$2_day",0),1);;\
  set_Reading("$1_$2_day",0,1);;\
  set_Reading("$1_$2_month",get_Reading("$1_$2_month",0)+get_Reading("$1_$2_last_day",0),1);;\
  if ($mday == 1) {\
    set_Reading("$1_$2_last_month",get_Reading("$1_$2_month",0),1);;\
    set_Reading("$1_$2_month",0,1);;\
    set_Reading("$1_$2_year",get_Reading("$1_$2_year",0)+get_Reading("$1_$2_last_month",0),1);;\
  }\
  if ($yday == 1) {\
    set_Reading("$1_$2_last_year",get_Reading("$1_$2_year",0),1);;\
    set_Reading("$1_$2_year",0,1);;\
  }\
}\
\
day_count_$1_$2 {set_Reading ("$1_$2_day",int(([$1:$2,0]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;}\
\
init_$1_$2 {\
  set_Reading("$1_$2_counter",[?$1:$2]) if (!get_Reading("$1_$2_counter",""));;\
}\
\
)\
TPL_stat (Stromzaehler,total_c)\
TPL_stat (Stromzaehler,total_f)\
TPL_stat (CUL_WZ,total)



Ein Zähler wird einfach mit TPL_stat (<device>,<reading>) angegeben. Im obigen Beispiel wurden drei Zähler definiert.

Damit kann man Tages-, Monats- oder Jahres-Verbräuche loggen oder in card darstellen, siehe Anhang:

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

gestein

Hallo Damian,

Danke für Dein Template für den Stromzähler.
Schön langsam verstehe ich wie das funktioniert.

Wann wird allerdings das Reading "$1_$2_day" gesetzt?
Ich weiß schon, in der Funktion ,,day_count_$1_$2".
Aber wer ruft die wann auf?

Danke, Lg, Gerhard

Damian

Zitat von: gestein am 16 Februar 2022, 00:31:29
Hallo Damian,

Danke für Dein Template für den Stromzähler.
Schön langsam verstehe ich wie das funktioniert.

Wann wird allerdings das Reading "$1_$2_day" gesetzt?
Ich weiß schon, in der Funktion ,,day_count_$1_$2".
Aber wer ruft die wann auf?

Danke, Lg, Gerhard

Zitatday_count_$1_$2 {set_Reading ("$1_$2_day",int(([$1:$2,0]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;}

Der Trigger ist also ein Event von $1=Device $2=Reading
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

gestein

Verstehe. Danke. Ganz schön komplex ...

Diese Schreibweise wäre also die kürzere Version von:
day_count_$1_$2 { [$1:$2]
   set_Reading ("$1_$2_day",int(([?$1:$2]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
}

Richtig?

Und wenn ich den Wert von "[?$1:$2]" in dieser Funktion mehrmals benötige, wie soll man dann am besten die Funktion schreiben?
z.B.:
day_count_$1_$2 { [$1:$2]
   set_Reading ("$1_$2_day",int(([?$1:$2]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
   set_Reading ("was anderes mit [?$1:$2]");
}

oder besser so:
day_count_$1_$2 {
   set_Reading ("$1_$2_day",int(([$1:$2,0]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
   set_Reading ("was anderes mit [?$1:$2]");
}


Danke für Deine Geduld.
lg, Gerhard

Damian

#244
Zitat von: gestein am 16 Februar 2022, 09:35:50
Verstehe. Danke. Ganz schön komplex ...

Diese Schreibweise wäre also die kürzere Version von:
day_count_$1_$2 { [$1:$2]
   set_Reading ("$1_$2_day",int(([?$1:$2]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
}

Richtig?
im Prinzip ja, da fehlt noch ein Semikolon hinter [$1:$2]. Da der Trigger überall im Block stehen kann, muss man den nicht gesondert angeben.
Zitat
Und wenn ich den Wert von "[?$1:$2]" in dieser Funktion mehrmals benötige, wie soll man dann am besten die Funktion schreiben?
z.B.:
day_count_$1_$2 { [$1:$2]
   set_Reading ("$1_$2_day",int(([?$1:$2]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
   set_Reading ("was anderes mit [?$1:$2]");
}

oder besser so:
day_count_$1_$2 {
   set_Reading ("$1_$2_day",int(([$1:$2,0]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;
   set_Reading ("was anderes mit [?$1:$2]");
}

Das ist egal, DOIF erkennt, dass es sich um den gleichen Trigger handelt, deswegen wird der Block nur einmal getriggert, egal wie oft du [$1:$2] innerhalb eines Blocks angibst.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

gestein

Danke, Damian.
So weit funktioniert das schon mal.
Ich hatte lange den Fehler gesucht, warum auf die Readings "energy" in den Shellys nicht im DOIF getriggert wird.
Ein "event-on-change" an der richtigen Stelle mit dem richtigen Wert hilft da ungemein ;)

Nun muss ich die Summen über Räume, Gruppen etc. machen.

Ist es eigentlich möglich eine Perl-Funktion ausserhalb des Templates zu definieren, die dann aus den Instanzen aufgerufen wird?
Diese Summen-Funktion soll ja nicht in jeder Instanz des Templates definiert sein.

Kann man eigentlich auf die Änderung eines Attributes des DOIF mit einem Funktionsaufruf reagieren?
Oder muss die Liste der zu bildenden Gruppen in einem Reading stehen?

Danke im Voraus
lg, Gerhard

Damian

Natürlich kannst du eine Perlfunktion in myUtils ablegen und diese aufrufen. Am besten davor noch package DOIF; angeben, damit sie direkt angegeben werden kann, sonst musst du :: vor die Funktion setzen, da sie sich sonst im package main befindet.

Du könntest für jede Gruppe ein DOIF erstellen und das gleiche Template per IMPORT-Befehl importieren.

Dann könntest du im Template die gleiche Aggregationsfunktion nutzen, die alle Readings des gleichen Typs wie last_day oder last_month addiert.

Das Reagieren auf Attributänderungen sollte über ein global-Event funktionieren. Musst du im Eventmonitor nachschauen, wenn sich ein Attribut ändert.

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

gestein

#247
Verstehe. Das sollte so klappen. Danke.

Prinzipiell wäre es aber auch möglich die Funktion im DOIF zu behalten und nicht ins 99_myUtils auszulagern.
Dann müsste ich sie in den uiTable-Teil geben. Oder?
Irgendwie habe ich gerne alles beisammen.

Kann man die Funktion dann aus den Instanzen der Templates heraus aufrufen?

Lg, Gerhard

Damian

Zitat von: gestein am 16 Februar 2022, 21:40:44
Verstehe. Das sollte so klappen. Danke.

Prinzipiell wäre es aber auch möglich die Funktion im DOIF zu behalten und nicht ins 99_myUtils auszulagern.
Dann müsste ich sie in den uiTable-Teil geben. Oder?
Irgendwie habe ich gerne alles beisammen.

Kann man die Funktion dann aus den Instanzen der Templates heraus aufrufen?

Lg, Gerhard

Eigene Funktionen im Block subs befinden sich im package DOIF und müssten in uiTable mit DOIF:: beginnen. Allerdings nutzen alle DOIF-Devices das gleiche package DOIF.

Wenn es um die Summation geht. Dafür reicht ja eine Zeile, wie ich sie dir bereits über die Aggregation aufgezeigt habe, dafür braucht man keine Funktion zu definieren.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

Damian

#249
So könnte das Codeschnipsel um einen sum-Block erweitert werden, der nach Mitternacht entsprechende Readings des DOIF-Devices zusammenaddiert.

defmod di_zaehler DOIF DEF TPL_stat (midnight_$1_$2 { [00:01];;\
  set_Reading("$1_$2_counter",[?$1:$2]);;\
  set_Reading("$1_$2_last_day",get_Reading("$1_$2_day",0),1);;\
  set_Reading("$1_$2_day",0,1);;\
  set_Reading("$1_$2_month",get_Reading("$1_$2_month",0)+get_Reading("$1_$2_last_day",0),1);;\
  \
  if ($mday == 1) {\
    set_Reading("$1_$2_last_month",get_Reading("$1_$2_month",0),1);;\
    set_Reading("$1_$2_month",0,1);;\
    set_Reading("$1_$2_year",get_Reading("$1_$2_year",0)+get_Reading("$1_$2_last_month",0),1);;\
  }\
  if ($yday == 1) {\
    set_Reading("$1_$2_last_year",get_Reading("$1_$2_year",0),1);;\
    set_Reading("$1_$2_year",0,1);;\
  }\
}\
\
day_count_$1_$2 {set_Reading ("$1_$2_day",int(([$1:$2,0]-get_Reading("$1_$2_counter",0))*1000)/1000,1);;}\
\
init_$1_$2 {\
  set_Reading("$1_$2_counter",[?$1:$2]) if (!get_Reading("$1_$2_counter",""));;\
  #set_Reading ("$1_$2_day",0);;\
  #set_Reading ("$1_$2_last_day",0);;\
  #set_Reading ("$1_$2_last_month",0);;\
  #set_Reading ("$1_$2_last_year",0);;\
}\
)\
\
sum {[00:02];;set_Reading("last_day_sum",[?#sum:"^$SELF$":"last_day$"],1);;\
  if ($mday == 1) {\
    set_Reading("last_month_sum",[?#sum:"^$SELF$":"last_month$"],1);;\
  }\
  if ($yday == 1) {\
    set_Reading("last_year_sum",[?#sum:"^$SELF$":"last_year$"],1);;\
  }\
}\
TPL_stat (test,state)\
TPL_stat (test,r1)\
TPL_stat (test,r2)
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF

gestein

Das wollte ich gerade fragen.
Wie immer bist Du schnell, diesmal sogar die Antwort bevor ich die Frage stellen konnte  ;)

Lg, Gerhard

Damian

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

Damian

Neue Version wurde eingecheckt: Nun werden die gesammelten Daten für card nicht gelöscht, wenn man die Definition per defmod ändert. So kann die Definition erweitert werden, ohne dass Datenverläufe in bestehenden cards verloren gehen.
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

Zitat von: Damian am 01 März 2022, 13:19:26
Energie wird nun teuer, da lohnt sich eine Analyse des eigenen Verbrauchs.

Der Wikieintrag zu dem Thema wurde aktualisiert:

https://wiki.fhem.de/wiki/DOIF/Automatisierung#Tages-.2C_Monats-_und_Jahresstatistik_f.C3.BCr_Strom-.2C_Gas-.2C_Wasserz.C3.A4hler_und_andere_Z.C3.A4hler

Ich habe den Code weiter verfeinert.
Dieser funktioniert nun auch, wenn der Zähler seinen aktuellen Zählerstand verliert, wie z. B. beim ESP8266.
Es werden jetzt zusätzlich automatisch Logs für Tages-, Monats- und Jahresverbräuche erzeugt.
Programmierte FHEM-Module: DOIF-FHEM, DOIF-Perl, DOIF-uiTable, THRESHOLD, FHEM-Befehl: IF