diff --git a/fhem/FHEM/91_notify.pm b/fhem/FHEM/91_notify.pm
index 3f5ad804b..0f7c2e33b 100644
--- a/fhem/FHEM/91_notify.pm
+++ b/fhem/FHEM/91_notify.pm
@@ -67,7 +67,8 @@ notify_Define($$)
$hash->{".COMMAND"} = $command;
my $doTrigger = ($name !~ m/^$re$/); # Forum #34516
- readingsSingleUpdate($hash, "state", "active", $doTrigger);
+ readingsSingleUpdate($hash, "state", "active", $doTrigger)
+ if(!$hash->{TEMPORARY});
InternalTimer(0, sub(){ notifyRegexpChanged($hash, $re); }, $hash);
return undef;
diff --git a/fhem/docs/commandref_frame.html b/fhem/docs/commandref_frame.html
index cab8ee287..fe91957e6 100644
--- a/fhem/docs/commandref_frame.html
+++ b/fhem/docs/commandref_frame.html
@@ -737,7 +737,7 @@ The following local attributes are used by a wider range of devices:
cancel [<id> [quiet]]
- Cancels a named sleep.
+ List named sleeps or cancel a named sleep.
@@ -1357,12 +1357,17 @@ The following local attributes are used by a wider range of devices:
sleep
- sleep <sec> [<id>] [quiet]
+ sleep <sec|timespec|regex> [<id>] [quiet]
+
sleep followed by another command is comparable to a nameless at, it executes the following commands after waiting the
- specified time. The unit is seconds, with millisecond accuracy, as you can
- specify decimal places.
+ href="#at">at or notify, it executes the
+ following commands after waiting for the specified time or an event matching
+ <regex>. The delay can be given
+ - in seconds, with millisecond accuracy, as you can specify decimal places,
+
+ - as a timespec (HH:MM or HH::MM::SS or {perlfunc})
+ - or as a regex (devicename or devicename:event)
A sleep with an <id> will replace a sleep with the same <id>
and can be canceled by cancel.
diff --git a/fhem/docs/commandref_frame_DE.html b/fhem/docs/commandref_frame_DE.html
index 02cb2198f..6c0c4b7a8 100644
--- a/fhem/docs/commandref_frame_DE.html
+++ b/fhem/docs/commandref_frame_DE.html
@@ -757,7 +757,7 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet:
cancel [<id> [quiet]]
- Entfernt ein benanntes sleep.
+ Listet benannte sleeps oder entfernt ein benanntes sleep.
@@ -1444,13 +1444,18 @@ Die folgenden lokalen Attribute werden von mehreren Geräten verwendet:
sleep
- sleep <sec> [<id>] [quiet]
+ sleep <sec|timespec|suchmuster> [<id>] [quiet]
sleep gefolgt von weiteren Befehlen ist vergleichbar mit einem namenlosen at Kommando, es führt die nachfolgenden Befehle aus,
- nachdem es die spezifizierte Zeitspanne gewartet hat. Die Einheit ist
- Sekunde, Millisekunden genau, da man Nachkommastellen spezifizieren
- kann.
+ href="#at">at oder notify Kommando, es führt
+ die nachfolgenden Befehle aus, nachdem es die spezifizierte Zeitspanne
+ gewartet hat bzw. ein Event welches dem <suchmuster> entspricht
+ aufgetreten ist. Die verzögerung kann
+ - in Sekunden (Millisekunden genau, da man Nachkommastellen spezifizieren
+ kann)
+ - als timespec (HH:MM or HH::MM::SS oder {perlfunc})
+ - oder als suchmuster (Gerätename oder Gerätename:Event)
+
angegeben werden.
Ein sleep mit einer <id> ersetzt ein sleep mit der gleichen <id>
and can mit cancel entfernt werden.
diff --git a/fhem/fhem.pl b/fhem/fhem.pl
index 503cda098..52044f066 100755
--- a/fhem/fhem.pl
+++ b/fhem/fhem.pl
@@ -106,7 +106,6 @@ sub SemicolonEscape($);
sub SignalHandling();
sub TimeNow();
sub Value($);
-sub WakeUpFn($);
sub WriteStatefile();
sub XmlEscape($);
sub addEvent($$);
@@ -2059,6 +2058,7 @@ CommandDefine($$)
if($currcfgfile ne AttrVal("global", "configfile", "") &&
!configDBUsed());
$hash{CL} = $cl;
+ $hash{TEMPORARY} = 1 if($temporary);
# If the device wants to issue initialization gets/sets, then it needs to be
# in the global hash.
@@ -2073,7 +2073,6 @@ CommandDefine($$)
} else {
delete $hash{CL};
- $hash{TEMPORARY} = 1 if($temporary);
foreach my $da (sort keys (%defaultattr)) { # Default attributes
CommandAttr($cl, "$name $da $defaultattr{$da}");
}
@@ -2082,8 +2081,10 @@ CommandDefine($$)
$modules{$m}{NotifyOrderPrefix} : "50-") . $name;
}
%ntfyHash = ();
- addStructChange("define", $name, $def);
- DoTrigger("global", "DEFINED $name", 1) if($init_done);
+ if(!$temporary && !$init_done) {
+ addStructChange("define", $name, $def);
+ DoTrigger("global", "DEFINED $name", 1);
+ }
}
return ($ret && $ignoreErr ?
"Cannot define $name, remove -ignoreErr for details" : $ret);
@@ -3133,15 +3134,19 @@ CommandTrigger($$)
#####################################
sub
-WakeUpFn($)
+sleep_WakeUpFn($)
{
- my $h = shift;
- delete $sleepers{$h->{id}} if( $h->{id} );
+ my $id = shift;
+ my $h = $sleepers{$id};
+ return if(!$h);
+ delete $sleepers{$id};
+ CommandDelete($h->{cl}, $h->{name}) if(!defined($h->{sec}));
$evalSpecials = $h->{evalSpecials};
my $ret = AnalyzeCommandChain($h->{cl}, $h->{cmd});
Log 2, "After sleep: $ret" if($ret && !$h->{quiet});
}
+
sub
CommandCancel($$)
{
@@ -3151,16 +3156,18 @@ CommandCancel($$)
if( !$id ) {
my $ret;
- foreach $id (keys %sleepers) {
+ foreach $id (sort keys %sleepers) {
+ my $h = $sleepers{$id};
$ret .= "\n" if( $ret );
- $ret .= sprintf( "%-10s %s", $id, $sleepers{$id}->{cmd} );
+ $ret .= sprintf( "%-12s %-19s %s", $id, $h->{till}, $h->{cmd} );
}
- $ret = "no pending sleeps" if( !$ret );
+ $ret = "no pending sleeps" if(!$ret);
return $ret;
} elsif( my $h = $sleepers{$id} ) {
- RemoveInternalTimer( $h );
- delete $sleepers{$h->{id}};
+ RemoveInternalTimer($id, "sleep_WakeUpFn") if(defined($h->{sec}));
+ CommandDelete($cl, $h->{name}) if(!defined($h->{sec}));
+ delete $sleepers{$id};
} else {
return "no such id: $id" if( !$quiet );
@@ -3179,24 +3186,47 @@ CommandSleep($$)
$quiet = $id;
$id = undef;
}
-
return "Argument missing" if(!defined($sec));
- return "Cannot interpret $sec as seconds" if($sec !~ m/^[0-9\.]+$/);
return "Last parameter must be quiet" if($quiet && $quiet ne "quiet");
- Log 4, "sleeping for $sec";
+ my $name = ".sleep_".(++$intAtCnt);
+ $id = $name if(!$id);
+
+ my $till;
+ if($sec !~ m/^[0-9\.]+$/) {
+ my ($err, $hr,$min,$s, $fn) = GetTimeSpec($sec);
+ if($err) { # not a valid timespec => treat as regex
+ if(@cmdList && $init_done) {
+ CommandDelete($cl, $name) if($defs{$name});
+ $err = CommandDefine($cl,
+ "-temporary $name notify $sec {sleep_WakeUpFn('$id')}");
+ $attr{$name}{ignore} = 1;
+ return $err if($err);
+ }
+ $till = $sec;
+ $sec = undef;
+
+ } else {
+ $sec = 3600*$hr+60*$min+$s;
+
+ }
+ }
+ $till = gettimeofday()+$sec if(defined($sec));
if(@cmdList && $init_done) {
my %h = (cmd => join(";", @cmdList),
evalSpecials => $evalSpecials,
quiet => $quiet,
+ till => defined($sec) ? FmtDateTime($till) : $till,
+ sec => $sec,
+ name => $name,
cl => $cl,
id => $id);
- if( $id ) {
- RemoveInternalTimer( $sleepers{$id} ) if( $sleepers{$id} );
- $sleepers{$id} = \%h;
+ if(defined($sec)) {
+ RemoveInternalTimer($id, "sleep_WakeUpFn");
+ InternalTimer($till, "sleep_WakeUpFn", $id, 0);
}
- InternalTimer(gettimeofday()+$sec, "WakeUpFn", \%h, 0);
+ $sleepers{$id} = \%h;
@cmdList=();
} else {