diff --git a/fhem/FHEM/98_DOIF.pm b/fhem/FHEM/98_DOIF.pm
index 3cfddad65..91e294daf 100644
--- a/fhem/FHEM/98_DOIF.pm
+++ b/fhem/FHEM/98_DOIF.pm
@@ -35,7 +35,7 @@ sub DOIF_delTimer($)
RemoveInternalTimer (\$hash->{triggertime}{$key});
}
foreach my $key (keys %{$hash->{ptimer}}) {
- RemoveInternalTimer ($key);
+ RemoveInternalTimer (\$hash->{ptimer}{$key});
}
}
@@ -1451,10 +1451,14 @@ sub ReplaceAllReadingsDoIf
}
} elsif ($condition >= 0) {
($timer,$err)=DOIF_CheckTimers($hash,$block,$condition,$trigger);
- return($timer,$err) if ($err);
- if ($timer) {
- $block=$timer;
- $event=1 if ($trigger);
+ if ($err eq "no timer") {
+ $block="[".$block."]";
+ } else {
+ return($timer,$err) if ($err);
+ if ($timer) {
+ $block=$timer;
+ $event=1 if ($trigger);
+ }
}
} else {
$block="[".$block."]";
@@ -1570,6 +1574,11 @@ DOIF_CheckTimers($$$$)
my $result;
my $end;
my $intervaltimer;
+
+ if ($timer !~ /^\s*(\[.*\]|\{.*\}|\(.*\)|\+.*|[0-9][0-9]:.*|:[0-5][0-9])$/ and $hash->{MODEL} eq "Perl") {
+ return ($timer,"no timer");
+ }
+
$timer =~ s/\s//g;
($timer,$days)=SplitDoIf('|',$timer);
$days="" if (!defined $days);
@@ -2799,6 +2808,66 @@ DOIF_SleepTrigger ($)
return undef;
}
+sub DOIF_set_Exec
+{
+ my ($hash,$timername,$seconds,$subname,$param)=@_;
+ $param="" if (!defined $param);
+ my $current = gettimeofday();
+ my $next_time = $current+$seconds;
+ $hash->{ptimer}{$timername}{time}=$next_time;
+ $hash->{ptimer}{$timername}{name}=$timername;
+ $hash->{ptimer}{$timername}{subname}=$subname;
+ $hash->{ptimer}{$timername}{param}=$param;
+ $hash->{ptimer}{$timername}{hash}=$hash;
+ RemoveInternalTimer(\$hash->{ptimer}{$timername});
+ if ($seconds > 0) {
+ readingsSingleUpdate ($hash,"timer_$timername",strftime("%d.%m.%Y %H:%M:%S",localtime($next_time)),0);
+ }
+ InternalTimer($next_time, "DOIF_ExecTimer",\$hash->{ptimer}{$timername}, 0);
+}
+
+
+sub DOIF_get_Exec
+{
+ my ($hash,$timername)=@_;
+ my $current = gettimeofday();
+ if (defined $hash->{ptimer}{$timername}{time}) {
+ my $sec=$hash->{ptimer}{$timername}{time}-$current;
+ if ($sec > 0) {
+ return ($sec);
+ } else {
+ delete ($hash->{ptimer}{$timername}{time});
+ return (0);
+ }
+ } else {
+ return (0);
+ }
+}
+
+sub DOIF_del_Exec
+{
+ my ($hash,$timername)=@_;
+ RemoveInternalTimer(\$hash->{ptimer}{$timername});
+ delete $hash->{ptimer}{$timername};
+ delete ($defs{$hash->{NAME}}{READINGS}{"timer_$timername"});
+}
+
+sub DOIF_ExecTimer
+{
+ my ($timer)=@_;
+ my $hash=${$timer}->{hash};
+ my $timername=${$timer}->{name};
+ my $name=$hash->{NAME};
+ my $subname=${$timer}->{subname};
+ my $param=${$timer}->{param};
+ eval ("$subname(\"$param\")");
+ if ($@) {
+ Log3 ($defs{$name}{NAME},1 , "$name error in $subname: $@");
+ readingsSingleUpdate ($hash, "error", "in $subname: $@",0);
+ }
+ delete ($defs{$name}{READINGS}{"timer_$timername"});
+}
+
sub DOIF_set_Timer
{
my ($hash,$event,$seconds)=@_;
@@ -2815,6 +2884,8 @@ sub DOIF_set_Timer
InternalTimer($next_time, "DOIF_PerlTimer", $timername, 0);
}
+
+
sub DOIF_get_Timer
{
my ($hash,$event)=@_;
@@ -2890,6 +2961,9 @@ CmdDoIfPerl($$)
$tail =~ s/set_Timer[ \t]*\(/DOIF_set_Timer\(\$hash,/g;
$tail =~ s/get_Timer[ \t]*\(/DOIF_get_Timer\(\$hash,/g;
$tail =~ s/del_Timer[ \t]*\(/DOIF_del_Timer\(\$hash,/g;
+ $tail =~ s/set_Exec[ \t]*\(/DOIF_set_Exec\(\$hash,/g;
+ $tail =~ s/get_Exec[ \t]*\(/DOIF_get_Exec\(\$hash,/g;
+ $tail =~ s/del_Exec[ \t]*\(/DOIF_del_Exec\(\$hash,/g;
$tail =~ s/set_Event[ \t]*\(/DOIF_set_Event\(\$hash,/g;
$tail =~ s/set_Reading[ \t]*\(/readingsSingleUpdate\(\$hash,/g;
$tail =~ s/\$_(\w+)/\$hash->\{var\}\{$1\}/g;
@@ -2897,12 +2971,22 @@ CmdDoIfPerl($$)
while ($tail ne "") {
($beginning,$perlblock,$err,$tail)=GetBlockDoIf($tail,'[\{\}]');
return ($perlblock,$err) if ($err);
- ($perlblock,$err)=ReplaceAllReadingsDoIf($hash,$perlblock,$i,0);
- return ($perlblock,$err) if ($err);
- $hash->{condition}{$i}=$perlblock;
if ($beginning =~ /(\w*)[\s]*$/) {
- $hash->{perlblock}{$i}=$1;
- if ($1 eq "init") {
+ my $blockname=$1;
+ if ($blockname eq "subs") {
+ $perlblock =~ s/\$SELF/$hash->{NAME}/g;
+ $perlblock ="no warnings 'redefine';".$perlblock;
+ eval ($perlblock);
+ if ($@) {
+ return ("error in defs block",$@);
+ }
+ next;
+ }
+ ($perlblock,$err)=ReplaceAllReadingsDoIf($hash,$perlblock,$i,0);
+ return ($perlblock,$err) if ($err);
+ $hash->{condition}{$i}=$perlblock;
+ $hash->{perlblock}{$i}=$blockname;
+ if ($blockname eq "init") {
$hash->{perlblock}{init}=$i;
if ($init_done) {
if (($ret,$err)=DOIF_CheckCond($hash,$hash->{perlblock}{init})) {
@@ -5403,7 +5487,8 @@ Syntax Perl-Modus:
define <name> DOIF <Blockname> {<Perl mit DOIF-Syntax in eckigen Klammern>} <Blockname> {<Perl mit DOIF-Syntax in eckigen Klammern>} ...fhem"..." ausgeführt. Im Gegensatz zum FHEM-Modus können im Perl-Modus mehrere Blöcke unabhängig voneinander, ausgelöst durch einen Ereignis- oder Zeit-Trigger, ausgeführt werden. So kann die Funktionalität mehrer DOIF-Module im FHEM-Modus innerhalb eines DOIF-Moduls im Perl-Moduls realisiert werden.set_Timer(<TimerEvent>, <seconds>), mit <TimerEvent>: beliebige Angabe, sie spezifiziert eindeutig einen Timer und ist gleichzeitig ein Ereignis,
welches nach Ablauf des Timers in FHEM erzeugt wird. Auf dieses Ereignis kann wie üblich mit der DOIF-Syntax durch die Angabe [$SELF:"^<TimerEvent>$"] reagiert werden.
Wird set_Timer mit dem gleichen <TimerEvent> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.get_Timer(<TimerEvent>), Returnwert: 0, w
del_Timer(<TimerEvent>)set_Exec(<timerName>, <seconds>, <function>, <parameter>), mit <timerName>: beliebige Angabe, sie spezifiziert eindeutig einen Timer,
+welcher nach Ablauf die angegebene Perl-Funktion <function> mit optionalen Parameter <parameter> aufruft. Die Perlfunkion muss eindeutig sein und in FHEM zuvor deklariert worden sein.
+Wird set_Exec mit dem gleichen <timerName> vor seinem Ablauf erneut aufgerufen, so wird der laufender Timer gelöscht und neugesetzt.get_Exec(<timerNamet>), Returnwert: 0, wenn Timer abgelaufen oder nicht gesetzt ist, sonst Anzahl der Sekunden bis zum Ablauf des Timersdel_Exec(<timerName>)set_Event(<Event>)set_Reading(<readingName>,<content>,<trigger>), mit <trigger>: 0 ohne Trigger, 1 mit Triggerif (defined $_...) ...
-define di_shutter DOIF { #Perlblock zur Auswertung des Tastendruckes
- if (["FS:^on$"] and get_Timer("Timer_shutter")==0){ #wenn Taste betätigt wird und kein Timer läuft
- set_Timer("Timer_shutter",2) #Timer für zwei Sekunden setzen
- } else { #wenn Timer läuft, d.h. ein weitere Tastendruck innerhalb von zwei Sekunden
- del_Timer("Timer_shutter") #Timer löschen
- fhem"set shutter up"; #Rollladen hoch
+define di_shutter DOIF { # Block zur Auswertung des Tastendruckes
+ if (["FS:^on$"] and get_Timer("Timer_shutter")==0){ # wenn Taste betätigt wird und kein Timer läuft
+ set_Timer("Timer_shutter",2); # Timer für zwei Sekunden setzen
+ } else { # wenn Timer läuft, d.h. ein weitere Tastendruck innerhalb von zwei Sekunden
+ del_Timer("Timer_shutter") # Timer löschen
+ fhem"set shutter up"; # Rollladen hoch
}
}
-{ #Perlblock für die Bearbeitung des Timerevents
- if ([$SELF:"Timer_shutter"]){ #Wenn nach zwei Sekunden Timer abläuft, d.h. nur ein Tastendruck
- fhem"set shutter down" #Rollladen runter
+{ # Block für die Bearbeitung des Timerevents
+ if ([$SELF:"Timer_shutter"]){ # wenn nach zwei Sekunden Timer abläuft, d.h. nur ein Tastendruck
+ fhem"set shutter down"; # Rollladen runter
}
}
@@ -5503,57 +5600,65 @@ define di_shutter DOIF { #Perlblock zur Auswertung des Tastendr
Im folgenden Beispiel wird die Nutzung von Instanzvariablen demonstriert.
-define di_count DOIF { #Perlblock zur Auswertung des Ereignisses
- if (["FS:on"] and get_Timer("Timer_counter")==0){ #wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft
- $_count=1; #setze count-Variable auf 1
- set_Timer("Timer_counter",3600) #setze Timer auf eine Stunde
+define di_count DOIF { # Block zur Auswertung des Ereignisses
+ if (["FS:on"] and get_Timer("Timer_counter")==0){ # wenn Ereignis (hier "FS:on") eintritt und kein Timer läuft
+ $_count=1; # setze count-Variable auf 1
+ set_Timer("Timer_counter",3600); # setze Timer auf eine Stunde
} else {
- $_count++ #wenn Timer bereits läuft zähle Ereignis
+ $_count++; # wenn Timer bereits läuft zähle Ereignis
}
}
-{ #Perlblock für die Auswertung nach Ablauf des Timers
- if ([$SELF:"Timer_counter"]) { #wenn Timer nach einer Stunde abläuft
- if ($_count > 10) {Log 3,"count: $_count action"}} #protokolliere im Log die Anzahl der Ereignisse, wenn sie über 10 ist
+{ # Block für die Auswertung nach Ablauf des Timers
+ if ([$SELF:"Timer_counter"]) { # wenn Timer nach einer Stunde abläuft
+ if ($_count > 10) {
+ Log 3,"count: $_count action"; # protokolliere im Log die Anzahl der Ereignisse, wenn sie über 10 ist
+ }
+ }
}
-define di_light DOIF bewegung { #Perlblock namens "bewegung" reagiert auf Bewegung von FS
- if (["FS:motion"]) {
- if ([?lamp:state] ne "on") { #wenn Lampe aus ist
- fhem"set lamp on"; #Lampe einschalten
- set_Reading ("state","on",1); #setze Status des DOIF-Moduls auf "on"
+
+define di_light DOIF
+subs { # Block "subs" zur Definition eigener Perl-Funktionen, hier ist nur Perl erlaubt ohne DOIF-Syntax
+ sub ein { # Perlfunktion "ein" zum Einschalten wird definiert
+ if (ReadingsVal ("lamp","state","") ne "on") {
+ fhem"set lamp on";
+ set_Reading ("state","on",1);
}
- set_Timer("lamp_off",30); #Timer wird gesetzt bzw. verlängert
+ }
+ sub aus { # Perlfunkton "aus" zum Ausschalten wird definiert
+ fhem"set lamp off";
+ set_Reading ("state","off",1);
}
}
-ausschalten { #Perlblock namens "ausschalten" reagiert auf Trigger vom des Timers "lamp_off"
- if ([$SELF:"lamp_off"]) { #Wenn Timer lamp_off abläuft
- fhem"set lamp off"; #schalte Lampe aus
- set_Reading ("state","off",1); #setze Status des DOIF-Modus auf "off"
+bewegung { # Block namens "bewegung" reagiert auf Bewegung von FS
+ if (["FS:motion"]) {
+ ein(); # Perlfunktion "ein" wird ausgeführt
+ set_Exec("light_off",10,"aus"); # Timer namens "light_off"für das Ausschalten über Perlfunktion "aus" wird gesetzt bzw. verlängert
}
-}
+}
+
define di_window DOIF {
- if (["_window$:open"]) { #wenn ein Fensterdevice endend mit "_window" geöffnet wird
- set_Timer ("WINDOW_$DEVICE",600) #setze einen Timer auf 10 Minuten
+define di_window DOIF { # Block zum Auswerten des Ereignisses: "Fenster geöffnet"
+ if (["_window$:open"]) { # wenn ein Fensterdevice endend mit "_window" geöffnet wird
+ set_Timer ("WINDOW_$DEVICE",600) # setze einen Timer auf 10 Minuten
}
}
-{ #Timer löschen, wenn Fenster geschlossen wird
+{ # Block zum Auswerten des Ereignisses: "Fenster gechlossen"
if (["_window$:closed"]) {
- del_Timer ("WINDOW_$DEVICE")
+ del_Timer ("WINDOW_$DEVICE") # Timer löschen, wenn Fenster geschlossen wird
}
}
-{ #Auswertung eines Timers
- if (["^$SELF:^WINDOW_"]) { #wenn ein Timerevent kommt
- my ($window,$device)=split("_","$EVENT"); #bestimme das Device aus dem Timerevent "WINDOW_$DEVICE"
- Log 3,"Fenster offen, bitte schließen: $device"; #Meldung wird protokolliert
- set_Timer ("WINDOW_$device",1800) #setze einen neuen Timer für das Fenster für eine erneute Meldung in 30 Minuten
+{ # Block zur Auswertung des zuvor gesetzten Timers
+ if (["^$SELF:^WINDOW_"]) { # wenn das zuvor gesetzte Timerevent kommt
+ my ($window,$device)=split("_","$EVENT"); # bestimme das Device aus dem Timerevent "WINDOW_$DEVICE"
+ Log 3,"Fenster offen, bitte schließen: $device"; # Meldung wird protokolliert
+ set_Timer ("WINDOW_$device",1800) # setze einen neuen Timer für das Fenster für eine erneute Meldung in 30 Minuten
}
}