| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- ##############################################
- # $Id: 91_watchdog.pm 16963 2018-07-09 07:40:22Z rudolfkoenig $
- package main;
- use strict;
- use warnings;
- use Time::HiRes qw(gettimeofday);
- #####################################
- sub
- watchdog_Initialize($)
- {
- my ($hash) = @_;
- $hash->{DefFn} = "watchdog_Define";
- $hash->{UndefFn} = "watchdog_Undef";
- $hash->{AttrFn} = "watchdog_Attr";
- $hash->{SetFn} = "watchdog_Set";
- $hash->{NotifyFn} = "watchdog_Notify";
- no warnings 'qw';
- my @attrList = qw(
- activateOnStart:0,1
- addStateEvent:0,1
- autoRestart:0,1
- disable:0,1
- disabledForIntervals
- execOnReactivate
- regexp1WontReactivate:0,1
- );
- use warnings 'qw';
- $hash->{AttrList} = join(" ", @attrList);
- # acivateOnStart handling
- InternalTimer(1, sub() {
- my @arr = devspec2array("TYPE=watchdog");
- my $now = time();
- foreach my $wd (@arr) {
- next if(!AttrVal($wd, "activateOnStart", 0));
- my $wh = $defs{$wd};
- my $aTime = ReadingsTimestamp($wd, "Activated", undef);
- my $tTime = ReadingsTimestamp($wd, "Triggered", undef);
- my $rTime = ReadingsTimestamp($wd, "Reset", undef);
- next if(!$aTime ||
- ($rTime && $rTime gt $aTime) ||
- ($tTime && $tTime gt $aTime) ||
- time_str2num($aTime)+$wh->{TO} <= $now);
- my $remaining = time_str2num($aTime)+$wh->{TO};
- watchdog_Activate($wh, $remaining);
- }
- }, 1) if(!$init_done);
- }
- sub
- watchdog_reset($)
- {
- my ($watchdog) = @_;
- $watchdog->{STATE} = "defined";
- setReadingsVal($watchdog, "Reset", "reset", TimeNow());
- }
- #####################################
- # defined watchme watchdog reg1 timeout reg2 command
- sub
- watchdog_Define($$)
- {
- my ($watchdog, $def) = @_;
- my ($name, $type, $re1, $to, $re2, $cmd) = split("[ \t]+", $def, 6);
-
- if(defined($watchdog->{TO})) { # modify
- $re1 = $watchdog->{RE1} if(!defined($re1));
- $to = $watchdog->{TO} if(!defined($to));
- $re2 = $watchdog->{RE2} if(!defined($re2));
- $cmd = $watchdog->{CMD} if(!defined($cmd));
- $watchdog->{DEF} = "$re1 $to $re2 $cmd";
- } else {
- return "Usage: define <name> watchdog <re1> <timeout> <re2> <command>"
- if(!$cmd);
- }
- # Checking for misleading regexps
- eval { "Hallo" =~ m/^$re1$/ };
- return "Bad regexp 1: $@" if($@);
- $re2 = $re1 if($re2 eq "SAME");
- eval { "Hallo" =~ m/^$re2$/ };
- return "Bad regexp 2: $@" if($@);
- return "Wrong timespec, must be HH:MM[:SS]"
- if($to !~ m/^(\d\d):(\d\d)(:\d\d)?$/);
- $to = $1*3600+$2*60+($3 ? substr($3,1) : 0);
- $watchdog->{RE1} = $re1;
- $watchdog->{RE2} = $re2;
- $watchdog->{TO} = $to;
- $watchdog->{CMD} = $cmd;
- if($re1 eq ".") {
- watchdog_Activate($watchdog)
- } else {
- $watchdog->{STATE} = "defined"; # do not set the reading
- }
- InternalTimer(1, sub($){
- my $re = ($re1 eq "." ? "" : $re1).
- (($re2 eq "SAME" || $re2 eq $re1) ? "" : "|$re2");
- $re .= "|$name"; # for trigger w .
- notifyRegexpChanged($watchdog, $re);
- }, $watchdog, 0);
- return undef;
- }
- #####################################
- sub
- watchdog_Notify($$)
- {
- my ($watchdog, $dev) = @_;
- my $ln = $watchdog->{NAME};
- return "" if(IsDisabled($ln) || $watchdog->{STATE} eq "inactive");
- my $dontReAct = AttrVal($ln, "regexp1WontReactivate", 0);
- my $n = $dev->{NAME};
- my $re1 = $watchdog->{RE1};
- my $re2 = $watchdog->{RE2};
-
- my $events = deviceEvents($dev, AttrVal($ln, "addStateEvent", 0));
- my $max = int(@{$events});
- for (my $i = 0; $i < $max; $i++) {
- my $s = $events->[$i];
- $s = "" if(!defined($s));
- my $dotTrigger = ($ln eq $n && $s eq "."); # trigger w .
- if($watchdog->{STATE} =~ m/Next:/) {
- if($dotTrigger) {
- RemoveInternalTimer($watchdog);
- watchdog_reset($watchdog);
- return;
- }
- if($n =~ m/^$re2$/ || "$n:$s" =~ m/^$re2$/) {
- my $isRe1 = ($re1 eq $re2 || $re1 eq ".");
- return if($isRe1 && $dontReAct); # 60414
- RemoveInternalTimer($watchdog);
- if($re1 eq $re2 || $re1 eq ".") {
- watchdog_Activate($watchdog);
- return "";
- } else {
- watchdog_reset($watchdog);
- }
- } elsif($n =~ m/^$re1$/ || "$n:$s" =~ m/^$re1$/) {
- watchdog_Activate($watchdog) if(!$dontReAct);
- }
- } elsif($watchdog->{STATE} eq "defined") {
- if($dotTrigger || ($n =~ m/^$re1$/ || "$n:$s" =~ m/^$re1$/)) {
- watchdog_Activate($watchdog)
- }
- } elsif($dotTrigger) {
- watchdog_reset($watchdog); # trigger w .
- }
- }
- return "";
- }
- sub
- watchdog_Trigger($)
- {
- my ($watchdog) = @_;
- my $name = $watchdog->{NAME};
- if(IsDisabled($name) || $watchdog->{STATE} eq "inactive") {
- watchdog_reset($watchdog);
- return "";
- }
- Log3 $name, 3, "Watchdog $name triggered";
- my $exec = SemicolonEscape($watchdog->{CMD});
- $watchdog->{STATE} = "triggered";
-
- setReadingsVal($watchdog, "Triggered", "triggered", TimeNow());
-
- my $ret = AnalyzeCommandChain(undef, $exec);
- Log3 $name, 3, $ret if($ret);
- if(AttrVal($name, "autoRestart", 0)) {
- watchdog_reset($watchdog); # auto trigger w .
- }
- }
- sub
- watchdog_Activate($;$)
- {
- my ($watchdog, $remaining) = @_;
- my $nt = ($remaining ? $remaining : gettimeofday() + $watchdog->{TO});
- $watchdog->{STATE} = "Next: " . FmtTime($nt);
- RemoveInternalTimer($watchdog);
- InternalTimer($nt, "watchdog_Trigger", $watchdog, 0);
- my $eor = AttrVal($watchdog->{NAME}, "execOnReactivate", undef);
- if($eor) {
- my $wName = $watchdog->{NAME};
- my $aTime = ReadingsTimestamp($wName, "Activated", "");
- my $tTime = ReadingsTimestamp($wName, "Triggered", "");
- $eor = undef if(!$aTime || !$tTime || $aTime ge $tTime)
- }
- setReadingsVal($watchdog, "Activated","activated", TimeNow()) if(!$remaining);
- AnalyzeCommandChain(undef, SemicolonEscape($eor)) if($eor);
- }
- sub
- watchdog_Undef($$)
- {
- my ($hash, $name) = @_;
- RemoveInternalTimer($hash);
- return undef;
- }
- sub
- watchdog_Attr(@)
- {
- my ($cmd, $name, $attrName, $attrVal) = @_;
- my $do = 0;
- my $hash = $defs{$name};
- if($cmd eq "set" && $attrName eq "disable") {
- $do = (!defined($attrVal) || $attrVal) ? 1 : 2;
- }
- $do = 2 if($cmd eq "del" && (!$attrName || $attrName eq "disable"));
- return if(!$do);
- $hash->{STATE} = ($do == 1 ? "disabled" : "defined");
- return undef;
- }
- sub
- watchdog_Set($@)
- {
- my ($hash, @a) = @_;
- my $me = $hash->{NAME};
- return "no set argument specified" if(int(@a) < 2);
- my %sets = (inactive=>0, active=>0);
-
- my $cmd = $a[1];
- return "Unknown argument $cmd, choose one of ".join(" ", sort keys %sets)
- if(!defined($sets{$cmd}));
- return "$cmd needs $sets{$cmd} parameter(s)" if(@a-$sets{$cmd} != 2);
- if($cmd eq "inactive") {
- readingsSingleUpdate($hash, "state", "inactive", 1);
- }
- elsif($cmd eq "active") {
- readingsSingleUpdate($hash, "state", "defined", 1)
- if(!AttrVal($me, "disable", undef));
- }
-
- return undef;
- }
- 1;
- =pod
- =item helper
- =item summary execute a command, if no event is received within timeout
- =item summary_DE führt Befehl aus, falls innerhalb des Timeouts kein Event empfangen wurde
- =begin html
- <a name="watchdog"></a>
- <h3>watchdog</h3>
- <ul>
- <br>
- <a name="watchdogdefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> watchdog <regexp1> <timespec> <regexp2> <command></code><br>
- <br>
- Start an arbitrary FHEM command if after <timespec> receiving an
- event matching <regexp1> no event matching <regexp2> is
- received.<br>
- The syntax for <regexp1> and <regexp2> is the same as the
- regexp for <a href="#notify">notify</a>.<br>
- <timespec> is HH:MM[:SS]<br>
- <command> is a usual fhem command like used in the <a
- href="#at">at</a> or <a href="#notify">notify</a>
- <br><br>
- Examples:
- <code><ul>
- # Request data from the FHT80 _once_ if we do not receive any message for<br>
- # 15 Minutes.<br>
- define w watchdog FHT80 00:15:00 SAME set FHT80 date<br>
- # Request data from the FHT80 _each_ time we do not receive any message for
- <br>
- # 15 Minutes, i.e. reactivate the watchdog after it triggered. Might be<br>
- # dangerous, as it can trigger in a loop.<br>
- define w watchdog FHT80 00:15:00 SAME set FHT80 date;; trigger w .<br>
- # Shout once if the HMS100-FIT is not alive<br>
- define w watchdog HMS100-FIT 01:00:00 SAME "alarm-fit.sh"<br>
- # Send mail if the window is left open<br>
- define w watchdog contact1:open 00:15 contact1:closed
- "mail_me close window1"<br>
- attr w regexp1WontReactivate<br>
- </ul></code>
- Notes:<br>
- <ul>
- <li>if <regexp1> is . (dot), then activate the watchdog at
- definition time. Else it will be activated when the first matching
- event is received.</li>
- <li><regexp1> resets the timer of a running watchdog, to avoid it
- use the regexp1WontReactivate attribute.</li>
- <li>if <regexp2> is SAME, then it will be the same as the first
- regexp, and it will be reactivated, when it is received.
- </li>
- <li>trigger <watchdogname> . will activate the trigger if its state
- is defined, and set it into state defined if its state is active
- (Next:...) or triggered. You always have to reactivate the watchdog
- with this command once it has triggered (unless you restart
- fhem)</li>
- <li>a generic watchdog (one watchdog responsible for more devices) is
- currently not possible.</li>
- <li>with modify all parameters are optional, and will not be changed if
- not specified.</li>
- </ul>
- <br>
- </ul>
- <a name="watchdogset"></a>
- <b>Set </b>
- <ul>
- <li>inactive<br>
- Inactivates the current device. Note the slight difference to the
- disable attribute: using set inactive the state is automatically saved
- to the statefile on shutdown, there is no explicit save necesary.<br>
- This command is intended to be used by scripts to temporarily
- deactivate the notify.<br>
- The concurrent setting of the disable attribute is not recommended.</li>
- <li>active<br>
- Activates the current device (see inactive).</li>
- </ul>
- <br>
- <a name="watchdogget"></a>
- <b>Get</b> <ul>N/A</ul><br>
- <a name="watchdogattr"></a>
- <b>Attributes</b>
- <ul>
- <li><a name="#activateOnStart">activateOnStart</a><br>
- if set, the watchdog will be activated after a FHEM start if appropriate,
- determined by the Timestamp of the Activated and the Triggered readings.
- Note: since between shutdown and startup events may be missed, this
- attribute is 0 (disabled) by default.
- </li>
- <li><a href="#addStateEvent">addStateEvent</a></li>
- <li><a href="#disable">disable</a></li>
- <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
- <li><a name="regexp1WontReactivate">regexp1WontReactivate</a><br>
- When a watchdog is active, a second event matching regexp1 will
- normally reset the timeout. Set this attribute to prevents this.</li>
- <li><a href="#execOnReactivate">execOnActivate</a>
- If set, its value will be executed as a FHEM command when the watchdog is
- reactivated (after triggering) by receiving an event matching regexp1.
- </li>
- <li><a href="#autoRestart">autoRestart</a>
- When the watchdog has triggered it will be automatically re-set to state
- defined again (waiting for regexp1) if this attribute is set to 1.
- </li>
- </ul>
- <br>
- </ul>
- =end html
- =begin html_DE
- <a name="watchdog"></a>
- <h3>watchdog</h3>
- <ul>
- <br>
- <a name="watchdogdefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> watchdog <regexp1> <timespec>
- <regexp2> <command></code><br>
- <br>
- Startet einen beliebigen FHEM Befehl wenn nach dem Empfang des
- Ereignisses <regexp1> nicht innerhalb von <timespec> ein
- <regexp2> Ereignis empfangen wird.<br>
- Der Syntax für <regexp1> und <regexp2> ist der gleiche wie
- regexp für <a href="#notify">notify</a>.<br>
- <timespec> ist HH:MM[:SS]<br>
- <command> ist ein gewöhnlicher fhem Befehl wie z.B. in <a
- href="#at">at</a> oderr <a href="#notify">notify</a>
- <br><br>
- Beispiele:
- <code><ul>
- # Frage Daten vom FHT80 _einmalig_ ab, wenn wir keine Nachricht für<br>
- # 15 Minuten erhalten haben.<br>
- define w watchdog FHT80 00:15:00 SAME set FHT80 date<br><br>
- # Frage Daten vom FHT80 jedes Mal ab, wenn keine Nachricht für<br>
- # 15 Minuten emfpangen wurde, d.h. reaktiviere den Watchdog nachdem er
- getriggert wurde.<br>
- # Kann gefährlich sein, da er so in einer Schleife getriggert werden
- kann.<br>
- define w watchdog FHT80 00:15:00 SAME set FHT80 date;; trigger w .<br><br>
- # Alarmiere einmalig wenn vom HMS100-FIT für eine Stunde keine
- Nachricht empfangen wurde.<br>
- define w watchdog HMS100-FIT 01:00 SAME "alarm-fit.sh"<br><br>
- # Sende eine Mail wenn das Fenster offen gelassen wurde<br>
- define w watchdog contact1:open 00:15 contact1:closed "mail_me close
- window1"<br>
- attr w regexp1WontReactivate<br><br>
- </ul></code>
- Hinweise:<br>
- <ul>
- <li>Wenn <regexp1> . (Punkt) ist, dann aktiviere den Watchdog zur
- definierten Zeit. Sonst wird er durch den Empfang des ersten passenden
- Events aktiviert.</li>
- <li><regexp1> Resetet den Timer eines laufenden Watchdogs. Um das
- zu verhindern wird das regexp1WontReactivate Attribut gesetzt.</li>
- <li>Wenn <regexp2> SAME ist , dann ist es das gleiche wie das erste
- regexp, und wird reaktiviert wenn es empfangen wird. </li>
- <li>trigger <watchdogname> . aktiviert den Trigger wenn dessen
- Status defined ist und setzt ihn in den Status defined wenn sein status
- triggered oder aktiviert (Next:...) ist.<br>
- Der Watchdog musst immer mit diesem Befehl reaktiviert werden wenn er
- getriggert wurde.</li>
- <li>Ein generischer Watchdog (ein Watchdog, verantwortlich für
- mehrere Devices) ist derzeit nicht möglich.</li>
- <li>Bei modify sind alle Parameter optional, und werden nicht geaendert,
- falls nicht spezifiziert.</li>
- </ul>
- <br>
- </ul>
- <a name="watchdogset"></a>
- <b>Set </b>
- <ul>
- <li>inactive<br>
- Deaktiviert das entsprechende Gerät. Beachte den leichten
- semantischen Unterschied zum disable Attribut: "set inactive"
- wird bei einem shutdown automatisch in fhem.state gespeichert, es ist
- kein save notwendig.<br>
- Der Einsatzzweck sind Skripte, um das notify temporär zu
- deaktivieren.<br>
- Das gleichzeitige Verwenden des disable Attributes wird nicht empfohlen.
- </li>
- <li>active<br>
- Aktiviert das entsprechende Gerät, siehe inactive.
- </li>
- </ul>
- <br>
- <a name="watchdogget"></a>
- <b>Get</b> <ul>N/A</ul><br>
- <a name="watchdogattr"></a>
- <b>Attribute</b>
- <ul>
- <li><a name="#activateOnStart">activateOnStart</a><br>
- Falls gesetzt, wird der Watchdog nach FHEM-Neustart aktiviert, je nach
- Zeitstempel der Activated und Triggered Readings. Da zwischen Shutdown
- und Neustart Events verlorengehen können, ist die Voreinstellung 0
- (deaktiviert).
- </li>
- <li><a href="#addStateEvent">addStateEvent</a></li>
- <li><a href="#disable">disable</a></li>
- <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
- <li><a name="regexp1WontReactivate">regexp1WontReactivate</a><br>
- Wenn ein Watchdog aktiv ist, wird ein zweites Ereignis das auf regexp1
- passt normalerweise den Timer zurücksetzen. Dieses Attribut wird
- das verhindern.</li>
- <li><a href="#execOnReactivate">execOnActivate</a>
- Falls gesetzt, wird der Wert des Attributes als FHEM Befehl
- ausgeführt, wenn ein regexp1 Ereignis den Watchdog
- aktiviert nachdem er ausgelöst wurde.</li>
- <li><a href="#autoRestart">autoRestart</a>
- Wenn dieses Attribut gesetzt ist, wird der Watchdog nach dem er
- getriggert wurde, automatisch wieder in den Zustand defined
- gesetzt (Wartet also wieder auf Aktivierung durch regexp1)</li>
- </ul>
- <br>
- </ul>
- =end html_DE
- =cut
|