From a01e9133d1c4a94e9ab3ba5ee24bf764dd6b549f Mon Sep 17 00:00:00 2001
From: damian-s <>
Date: Sat, 7 Mar 2015 17:10:02 +0000
Subject: [PATCH] =?UTF-8?q?98=5FDOIF.pm:=20Indirekte=20Zeitangaben,=20Unte?=
=?UTF-8?q?rst=C3=BCtzung=20von=20readingFnAttributes,=20$Id-String=20eing?=
=?UTF-8?q?er=C3=BCckt?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
git-svn-id: https://svn.fhem.de/fhem/trunk@8166 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/98_DOIF.pm | 154 ++++++++++++++++++++++++++++++++++---------
1 file changed, 122 insertions(+), 32 deletions(-)
diff --git a/fhem/FHEM/98_DOIF.pm b/fhem/FHEM/98_DOIF.pm
index 121645127..6ba9f9e2f 100644
--- a/fhem/FHEM/98_DOIF.pm
+++ b/fhem/FHEM/98_DOIF.pm
@@ -1,20 +1,19 @@
##############################################
-# $Id$
-#
-# This file is part of fhem.
-#
-# Fhem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# Fhem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with fhem. If not, see .
+# $Id$
+#
+# This file is part of fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
#
###############################################
@@ -35,7 +34,7 @@ DOIF_Initialize($)
$hash->{UndefFn} = "DOIF_Undef";
$hash->{AttrFn} = "DOIF_Attr";
$hash->{NotifyFn} = "DOIF_Notify";
- $hash->{AttrList} = "disable:0,1 loglevel:0,1,2,3,4,5,6 wait do:always,resetwait cmdState state initialize repeatsame waitsame waitdel cmdpause";
+ $hash->{AttrList} = "disable:0,1 loglevel:0,1,2,3,4,5,6 wait do:always,resetwait cmdState state initialize repeatsame waitsame waitdel cmdpause ".$readingFnAttributes;
}
@@ -276,6 +275,8 @@ sub ReplaceAllReadingsDoIf($$$$)
$event=1;
} elsif ($condition == -2) {
$hash->{state}{device} = AddItemDoIf($hash->{state}{device},$device) if ($device ne $hash->{NAME});
+ } elsif ($condition == -3) {
+ $hash->{itimer}{all} = AddItemDoIf($hash->{itimer}{all},$device);
}
}
} else {
@@ -374,8 +375,9 @@ DOIF_CheckTimers($$$$$)
my $pos;
my $time;
my $block;
+ my $result;
my ($hash,$timer,$condition,$trigger,$timerarray)=@_;
- if ($timer =~ /^(\+)?((\{.*\})|([0-9][0-9](:[0-5][0-9]){1,2}))(\|[0-8]+$|-(\+)?([0-9][0-9](:[0-5][0-9]){1,2})|-(\+)?({.*})|$)(\|[0-8]+$|$)/) {
+ if ($timer =~ /^(\+)?((\{.*\})|(\[.*\])|([0-9][0-9](:[0-5][0-9]){1,2}))(\|[0-8]+$|-(\+)?(([0-9][0-9](:[0-5][0-9]){1,2})|({.*})|(\[.*\]))|$)(\|[0-8]+$|$)/) {
while ($timer ne "") {
if ($timer=~ /^\s*\{/) {
($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\{\}]');
@@ -386,6 +388,17 @@ DOIF_CheckTimers($$$$$)
$days=substr($timer,$pos);
$timer="";
}
+ } elsif ($timer=~ /^\s*\[/) {
+ ($beginning,$time,$err,$timer)=GetBlockDoIf($timer,'[\[\]]');
+ return ($time,$err) if ($err);
+ $time="[".$time."]";
+ ($result,$err)=ReplaceAllReadingsDoIf($hash,$time,-3,0);
+ return ($time,$err) if ($err);
+ if ($timer =~ /^\s*\|/g) {
+ $pos=pos($timer);
+ $days=substr($timer,$pos);
+ $timer="";
+ }
} elsif ($timer =~ /-/g) {
$pos=pos($timer)-1;
$time=substr($timer,0,$pos);
@@ -408,8 +421,10 @@ DOIF_CheckTimers($$$$$)
$hash->{timeCond}{$nr}=$condition;
$hash->{days}{$nr}=$days if ($days ne "");
${$timerarray}[$nr]={hash=>$hash,nr=>$nr};
- $err=(DOIF_SetTimer("DOIF_TimerTrigger",\${$timerarray}[$nr]));
- return($hash->{time}{$nr},$err) if ($err);
+ if ($init_done) {
+ $err=(DOIF_SetTimer("DOIF_TimerTrigger",\${$timerarray}[$nr]));
+ return($hash->{time}{$nr},$err) if ($err);
+ }
$hash->{timers}{$condition}.=" $nr " if ($trigger);
$hash->{timerfunc}{$nr}=\${$timerarray}[$nr];
}
@@ -420,6 +435,7 @@ DOIF_CheckTimers($$$$$)
}
return ($block,"");
}
+ delete ($hash->{helper}{modify});
return("","");
}
@@ -673,25 +689,47 @@ DOIF_Notify($$)
my ($hash, $dev) = @_;
my $pn = $hash->{NAME};
return "" if($attr{$pn} && $attr{$pn}{disable});
- return "" if (!$hash->{devices}{all} and !$hash->{state}{device}) ;
return "" if (!$dev->{NAME});
my $device;
my $reading;
my $internal;
my $ret;
+ my $err;
+
+ if ($dev->{NAME} eq "global" and ((EventDoIf("global","global",deviceEvents($dev, AttrVal("global", "addStateEvent", 0)),"INITIALIZED")) or EventDoIf("global","global",deviceEvents($dev, AttrVal("global", "addStateEvent", 0)),"REREADCFG")))
+ {
+ if ($hash->{helper}{last_timer} > 0){
+ for (my $j=0; $j<$hash->{helper}{last_timer};$j++)
+ {
+ DOIF_SetTimer("DOIF_TimerTrigger",$hash->{timerfunc}{$j});
+ }
+ }
+ return undef;
+ }
+
+ if (($hash->{itimer}{all}) and $hash->{itimer}{all} =~ / $dev->{NAME} /) {
+ for (my $j=0; $j<$hash->{helper}{last_timer};$j++)
+ {
+ if ($hash->{time}{$j} =~ /\[$dev->{NAME}\]|\[$dev->{NAME}:/) {
+ DOIF_SetTimer("DOIF_TimerTrigger",$hash->{timerfunc}{$j});
+ }
+ }
+ }
+
+ return "" if (!$hash->{devices}{all} and !$hash->{state}{device}) ;
if (($hash->{devices}{all}) and $hash->{devices}{all} =~ / $dev->{NAME} /) {
if ($hash->{readings}{all}) {
foreach my $item (split(/ /,$hash->{readings}{all})) {
($device,$reading)=(split(":",$item));
- readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$reading,$defs{$device}{READINGS}{$reading}{VAL},0) if ($item and $device eq $dev->{NAME} and $defs{$device}{READINGS}{$reading});
+ readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$reading,$defs{$device}{READINGS}{$reading}{VAL},0) if ($item and $device eq $dev->{NAME} and defined ($defs{$device}{READINGS}{$reading}));
}
}
if ($hash->{internals}{all}) {
foreach my $item (split(/ /,$hash->{internals}{all})) {
($device,$internal)=(split(":",$item));
- readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$internal,$defs{$device}{$internal},0) if ($item and $device eq $dev->{NAME} and $defs{$device}{$internal});
+ readingsSingleUpdate ($hash, "e_".$dev->{NAME}."_".$internal,$defs{$device}{$internal},0) if ($item and $device eq $dev->{NAME} and defined ($defs{$device}{$internal}));
}
}
#my ($seconds, $microseconds) = gettimeofday();
@@ -711,7 +749,7 @@ DOIF_Notify($$)
}
if (($hash->{state}{device}) and $hash->{state}{device} =~ / $dev->{NAME} / and !$ret) {
DOIF_SetState($hash,"","","");
- }
+ }
return undef;
}
@@ -745,7 +783,25 @@ DOIF_SetTimer($$)
$timeStr=substr($timeStr,1);
$rel=1;
}
+ if ($timeStr=~ /^\s*\[/) {
+ my $err;
+ ($timeStr,$err)=ReplaceAllReadingsDoIf($hash,$timeStr,-3,1);
+ if ($err)
+ {
+ readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),"error: ".$err,0);
+ RemoveInternalTimer($timer);
+ $hash->{realtime}{$nr}="00:00:00";
+ return $err;
+ }
+ }
my ($err, $h, $m, $s, $fn) = GetTimeSpec($timeStr);
+ if ($err)
+ {
+ readingsSingleUpdate ($hash,"timer_".($nr+1)."_c".($cond+1),"error: ".$err,0);
+ RemoveInternalTimer($timer);
+ $hash->{realtime}{$nr}="00:00:00";
+ return $err;
+ }
return $err if($err);
my $second = $h*3600+$m*60+$s;
#my $now = time();
@@ -859,7 +915,6 @@ CmdDoIf($$)
} else {
$tail =~ s/\n//g;
}
-
if (defined $hash->{helper}) #def modify
{
RemoveInternalTimer($hash);
@@ -875,6 +930,7 @@ CmdDoIf($$)
delete ($hash->{time});
delete ($hash->{timer});
delete ($hash->{timers});
+ delete ($hash->{itimer});
delete ($hash->{timeCond});
delete ($hash->{realtime});
delete ($hash->{days});
@@ -1054,7 +1110,7 @@ The commands are always processed from left to right. There is only one command
+ it can be any FHEM commands and perl commands are executed
+ syntax checking at the time of definition are identified missing brackets
+ status is specified with [<devicename>]
, readings with [<devicename>:<readingname>]
or internals with [<devicename>:&<internal>]
-+ time information on the condition: [HH:MM:SS]
or [HH:MM]
or [{<perl-function>}]
++ time information on the condition: [HH:MM:SS]
or [HH:MM]
or [[<devicename>]]
or [[<devicename>:<readingname>]]
or [{<perl-function>}]
+ time intervals: [<begin>-<end>]
for <begin>
and <end>
, the above time format can be selected.
+ relative times preceded by a plus sign [+<time>]
or [+<begin>-+<end>]
combined with Perl functions
+ weekday control: [<time>|012345678]
or [<begin>-<end>|012345678]
(0-6 corresponds to Sunday through Saturday) such as 7 for $we and 8 for !$we
@@ -1097,7 +1153,7 @@ Die Angaben werden immer von links nach rechts abgearbeitet. Es wird immer nur e
+ Bedingungen werden vom Perl-Interpreter ausgewertet, daher beliebige logische Abfragen möglich
+ Die Perl-Syntax wird um Angaben von Stati, Readings, Internals, Events oder Zeitangaben in eckigen Klammern erweitert, diese führen zur Triggerung des Moduls
+ Stati werden mit [<devicename>]
, Readings mit [<devicename>:<readingname>]
, Internals mit [<devicename>:&<internal>]
oder Events mit [<devicename>:?<regexp>]
angegeben
-+ Zeitangaben in der Bedingung: [HH:MM:SS]
oder [HH:MM]
oder [{<perl-function>}]
++ Zeitangaben in der Bedingung: [HH:MM:SS]
oder [HH:MM]
oder [[<devicename>]]
oder [[<devicename>:<readingname>]]
oder [{<perl-function>}]
+ Zeitintervalle: [<begin>-<end>]
für <begin>
bzw. <end>
kann das obige Zeitformat gewählt werden
+ relative Zeitangaben mit vorangestelltem Pluszeichen [+<time>]
oder [+<begin>-+<end>]
kombinierbar mit Perl-Funktionen s. o.
+ Wochentagsteuerung: [<time>|012345678]
oder [<begin>-<end>|012345678]
(0-6 entspricht Sonntag bis Samstag) sowie 7 für $we und 8 für !$we
@@ -1216,10 +1272,36 @@ Einschalten am Freitag ausschalten am Montag:
define di_light DOIF ([22:00|5]) (set light on) DOELSEIF ([10:00|1]) (set light off)
-Schalten bei Sonnenaufgang und Sonnenuntergang:
+Schalten mit Zeitfunktionen, hier: bei Sonnenaufgang und Sonnenuntergang:
define di_light DOIF ([{sunrise_abs()}-{sunset(1800,"17:00","21:00")}])(set outdoorlight off) DOELSE (set outdoorlight on)
+Schalten mit indirekten Zeitangaben
+
+Statt fester Zeitangaben können auch Stati, Readings oder Internals angegeben werden. Diese müssen eine Zeitangabe im Format HH:MM oder HH:MM:SS beinhalten.
+
+Anwendungsbeispiel
+
+define time dummy
+set time 08:00
+define di_time DOIF ([[time]])(set lamp on)
+
+oder Angaben mit Zeitintervallen
+
+define begin dummy
+set begin 08:00
+
+define end dummy
+set end 10:00
+
+define di_time DOIF ([[begin]-[end]])(set radio on) DOELSE (set radio off)
+
+Bei einer Änderung des angebenen Status oder Readings wird die geänderte Zeit sofort im Modul aktualisiert.
+
+Die Endzeit soll abhängig von der Beginnzeit mit Hilfe einer eignen Perl-Funktion, hier: OffTime()
, bestimmt werden. begin
und end
sind Dummys, wie oben definiert:
+
+define di_time DOIF ([[begin]-[end]]) (set lamp on, set end_time {(OffTime("[begin]"))}) DOELSE (set lamp off)
+
Kombination von Ereignis- und Zeitsteuerung mit logischen Abfragen
Anwendungsbeispiel: Lampe soll ab 6:00 Uhr angehen, wenn es dunkel ist und wieder ausgehen, wenn es hell wird, spätestens aber um 9:00 Uhr:
@@ -1234,9 +1316,9 @@ Schalten bei Sonnenaufgang und Sonnenuntergang:
Angaben in eckigen Klammern, die mit einem Fragezeichen beginnen, führen zu keiner Triggerung des Moduls, sie dienen lediglich der Abfrage.
-Anwendungsbeispiel: Licht soll zwischen 06:00 und 10:00 angehen, getriggert wird nur durch den Bewegungssensor nicht um 06:00 bzw. 10:00 Uhr
+Anwendungsbeispiel: Licht soll zwischen 06:00 und 10:00 angehen, getriggert wird nur durch den Taster nicht um 06:00 bzw. 10:00 Uhr
-define di_motion DOIF ([?06:00-10:00] and [BM] eq "motion")(set lamp on-for-timer 600)
+define di_motion DOIF ([?06:00-10:00] and [button])(set lamp on-for-timer 600)
attr di_motion do always
Nutzung von Readings, Stati oder Internals im Ausführungsteil
@@ -1280,11 +1362,19 @@ attr di_washer wait 300
Eine erneute Benachrichtigung wird erst wieder ausgelöst, wenn zwischendurch der Verbrauch über 2 Watt angestiegen war.
-Rolladen um 20 Minuten zeitverzögert bei Sonne runter- bzw. hochfahren (wenn der Zustand der Sonne wechselt, wird die Verzögerungszeit zurückgesetzt):
+Anwendungsbeispiel: Rolladen um 20 Minuten zeitverzögert bei Sonne runter- bzw. hochfahren (wenn der Zustand der Sonne wechselt, wird die Verzögerungszeit zurückgesetzt):
define di_shutters DOIF ([Sun] eq "on") (set shutters down) DOELSE (set shutters up)
attr di_shutters wait 1200:1200
+Anwendungsbeispiel: Rolladen nach Sonnenuntergang mit Zufallsverzögerung von 10 bis 20 Minuten herunterfahren:
+
+define di_shutters DOIF ([{sunset_abs()}]) (set shutters down, attr di_shutters wait {(600+int(rand(600)))})
+attr di_shutters do always
+attr di_shutters wait 600
+
+Hier wird der wait-timer per wait-Attribut für die nächste Ausführung per Zufall vorbereitet.
+
Anwendungsbeispiel: Beschattungssteuerung abhängig von der Temperatur. Der Rollladen soll runter von 11:00 Uhr bis Sonnenuntergang, wenn die Temperatur über 26 Grad ist. Temperaturschwankungen um 26 Grad werden mit Hilfe des wait-Attributes durch eine 15 minutige Verzögerung ausgeglichen.
define di_shutters DOIF ([sensor:temperature] > 26 and [11:00-{sunset_abs()}] (set shutters down) DOELSE (set shutters up)
@@ -1458,7 +1548,7 @@ Die Nachbildung eines on-for-timers lässt sich mit zwei DOIF´s und einem Dummy
define switch_d dummy
-define di_switch DOIF ([detector] eq "motion") (set switch_d on, set switch_d off)
+define di_switch DOIF ([detector:?motion]) (set switch_d on, set switch_d off)
attr di_switch do always
define di_light DOIF ([switch_d] eq "on") (set light on) DOELSE (set light off)