From f05dc97b8fe6d8b77ab52d9d8b46e01e3ad76611 Mon Sep 17 00:00:00 2001 From: Damian Date: Thu, 17 Sep 2020 15:31:13 +0000 Subject: [PATCH] 98_DOIF.pm: set_Exec with condition git-svn-id: https://svn.fhem.de/fhem/trunk@22781 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/98_DOIF.pm | 142 +++++++++++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 39 deletions(-) diff --git a/fhem/FHEM/98_DOIF.pm b/fhem/FHEM/98_DOIF.pm index a087de56f..574671472 100644 --- a/fhem/FHEM/98_DOIF.pm +++ b/fhem/FHEM/98_DOIF.pm @@ -1687,8 +1687,8 @@ sub ReplaceAllReadingsDoIf $block="[".$block."]"; } } else { - $trigger=0 if (substr($block,0,1) eq "\$"); - if ($block =~ /^\$?[a-z0-9._]*[a-z._]+[a-z0-9._]*($|:.+$|,.+$)/i) { + $trigger=0 if (substr($block,0,7) eq "\$DEVICE"); + if ($block =~ /^(\$DEVICE|[a-z0-9._]*[a-z._]+[a-z0-9._]*)($|:.+$|,.+$)/i) { ($block,$err,$device,$reading,$internal)=ReplaceReadingEvalDoIf($hash,$block,$eval); return ($block,$err) if ($err); if ($condition >= 0) { @@ -3799,38 +3799,78 @@ sub DOIF_ExecTimer my $timername=${$timer}->{name}; my $name=$hash->{NAME}; my $subname=${$timer}->{subname}; - my $param=${$timer}->{param} if (defined ${$timer}->{param}); + my $count=${$timer}->{count}; + my $condition=${$timer}->{cond}; my $cur_hs=$hs; $hs=$hash; delete ($::defs{$name}{READINGS}{"timer_$timername"}); - if (!defined ($param)) { - eval ("package DOIF;$subname"); - } else { - #eval ("package DOIF;$subname(\"$param\")"); - eval('package DOIF;no strict "refs";&{$subname}($param);use strict "refs"'); + if (defined ($condition) and !eval ($condition)) { + $hs=$cur_hs; + return (0); } + eval ("package DOIF;$subname"); if ($@) { - ::Log3 ($::defs{$name}{NAME},1 , "$name error in $subname: $@"); + ::Log3 ($hash->{NAME},1 , "$name error in $subname: $@"); ::readingsSingleUpdate ($hash, "error", "in $subname: $@",1); } + if (defined ($condition)) { + $count=++${$timer}->{count}; + if (!eval ($condition)) { + $hs=$cur_hs; + return (0); + } + } else { + $hs=$cur_hs; + return (0); + } + my $current = ::gettimeofday(); + my $seconds=eval (${$timer}->{sec}); + my $next_time = $current+$seconds; + ${$timer}->{time}=$next_time; + if ($seconds > 0) { + ::readingsSingleUpdate ($hash,"timer_$timername",::strftime("%d.%m.%Y %H:%M:%S",localtime($next_time)),0); + } + ::InternalTimer($next_time, "DOIF::DOIF_ExecTimer",$timer, 0); $hs=$cur_hs; + return(0); } sub set_Exec { - my ($timername,$seconds,$subname,$param)=@_; + my ($timername,$sec,$subname,$condition)=@_; + my $count=0; + $hs->{ptimer}{$timername}{sec}=$sec; + $hs->{ptimer}{$timername}{name}=$timername; + $hs->{ptimer}{$timername}{subname}=$subname; + $hs->{ptimer}{$timername}{cond}=$condition if (defined $condition); + #$hs->{ptimer}{$timername}{param}=$param if (defined $param); + $hs->{ptimer}{$timername}{count}=$count; + $hs->{ptimer}{$timername}{hash}=$hs; + ::RemoveInternalTimer(\$hs->{ptimer}{$timername}); + if (defined ($condition)) { + my $cond=eval ($condition); + if ($@) { + ::Log3 ($hs->{NAME},1,"$hs->{NAME} error eval condition: $@"); + ::readingsSingleUpdate ($hs, "error", "eval condition: $@",1); + return (1); + } + if (!$cond) { + return (0); + } + } + my $seconds=eval($sec); + if ($@) { + ::Log3 ($hs->{NAME},1,"$hs->{NAME} error eval seconds: $@"); + ::readingsSingleUpdate ($hs, "error", "eval seconds : $@",1); + return(1); + } my $current = ::gettimeofday(); my $next_time = $current+$seconds; $hs->{ptimer}{$timername}{time}=$next_time; - $hs->{ptimer}{$timername}{name}=$timername; - $hs->{ptimer}{$timername}{subname}=$subname; - $hs->{ptimer}{$timername}{param}=$param if (defined $param); - $hs->{ptimer}{$timername}{hash}=$hs; - ::RemoveInternalTimer(\$hs->{ptimer}{$timername}); if ($seconds > 0) { ::readingsSingleUpdate ($hs,"timer_$timername",::strftime("%d.%m.%Y %H:%M:%S",localtime($next_time)),0); } - ::InternalTimer($next_time, "DOIF::DOIF_ExecTimer",\$hs->{ptimer}{$timername}, 0); + ::InternalTimer($next_time, "DOIF::DOIF_ExecTimer",\$hs->{ptimer}{$timername}, 0); } sub get_Exec @@ -6978,11 +7018,17 @@ Die Readings "temperature" und "humidity" sollen in einem Eventblock mit dem zuv
Ausführungstimer   back

-Mit Hilfe von Ausführungstimern können Anweisungen verzögert ausgeführt werden. Im Gegensatz zum FHEM-Modus können beliebig viele Timer gleichzeitig genutzt werden. +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.

-Timer setzen: set_Exec(<timerName>, <seconds>, <perlCode>, <parameter>), mit <timerName>: beliebige Angabe, sie spezifiziert eindeutig einen Timer, -welcher nach Ablauf den angegebenen Perlcode <perlCode> aufruft. Falls als Perlcode eine Perlfunktion angegeben wird, kann optional ein Übergabeparameter <parameter> angegeben werden. Die Perlfunkion muss eindeutig sein und in FHEM zuvor deklariert worden sein. +Timer setzen: set_Exec(<timerName>, <seconds>, <perlCode>, <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
+<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 <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.

Timer holen: get_Exec(<timerName>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timers
@@ -6991,19 +7037,52 @@ Laufenden Timer löschen: del_Exec(<timerName>)

Beispiel: Funktion namens "lamp" mit dem Übergabeparameter "on" 30 Sekunden verzögert aufrufen:

-set_Exec("lamp_timer",30,'lamp','on');
-
-alternativ
-
set_Exec("lamp_timer",30,'lamp("on")');

Beispiel: Lampe verzögert um 30 Sekunden ausschalten:

-set_Exec("off",30,'fhem_set("lamp off")');
+set_Exec("off_timer",30,'fhem_set("lamp off")');

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

set_Exec("off_Event",30,'set_Event("off")');
+
+Anwendungsbeispiele mit bedingter Wiederholung einer Ausführung
+
+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"')}
+
+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 mit 0 nach jedem Durchlauf um eins erhöht wird.
+
+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")\
+}\
+}

+
+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')}
+
+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.
+

init-Block   back

@@ -7116,21 +7195,6 @@ define di_count DOIF {
  }
}
-
-Verzögerte Fenster-offen-Meldung mit Wiederholung für mehrere Fenster
-
- -define di_window DOIF
-subs {
-  sub logwin {                                       # Definition der Funktion namens "logwin"
-    my ($window)=@_;                                 # übernehme Parameter in die Variable $window
-    Log 3,"Fenster offen, bitte schließen: $window"; # protokolliere Fenster-Offen-Meldung
-    set_Exec ("$window",1800,"logwin",$window);      # setze Timer auf 30 Minuten für eine wiederholte Meldung
-  }
-}
-{ if (["_window$:open"]) {set_Exec ("$DEVICE",600,'logwin',"$DEVICE")}} # wenn, Fenster geöffnet wird, dann setze Timer auf Funktion zum Loggen namens "logwin"
-{ if (["_window$:closed"]) {del_Exec ("$DEVICE")}}                       # wenn, Fenster geschlossen wird, dann lösche Timer
-
=end html_DE =cut