############################################## # $Id$ 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->{NotifyFn} = "watchdog_Notify"; $hash->{AttrList} = "disable:0,1 disabledForIntervals execOnReactivate ". "regexp1WontReactivate:0,1 addStateEvent:0,1 ". "autoRestart:0,1"; } ##################################### # 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 watchdog " 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"; } return undef; } ##################################### sub watchdog_Notify($$) { my ($watchdog, $dev) = @_; my $ln = $watchdog->{NAME}; return "" if(IsDisabled($ln)); 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->{STATE} = "defined"; return; } if($n =~ m/^$re2$/ || "$n:$s" =~ m/^$re2$/) { RemoveInternalTimer($watchdog); if(($re1 eq $re2 || $re1 eq ".") && !$dontReAct) { watchdog_Activate($watchdog); return ""; } else { $watchdog->{STATE} = "defined"; } } 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->{STATE} = "defined"; # trigger w . } } return ""; } sub watchdog_Trigger($) { my ($watchdog) = @_; my $name = $watchdog->{NAME}; if(AttrVal($name, "disable", 0)) { $watchdog->{STATE} = "defined"; 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->{STATE} = "defined"; # auto trigger w . } } sub watchdog_Activate($) { my ($watchdog) = @_; my $nt = 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()); 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; } 1; =pod =item helper =begin html

watchdog


    Define
      define <name> watchdog <regexp1> <timespec> <regexp2> <command>

      Start an arbitrary FHEM command if after <timespec> receiving an event matching <regexp1> no event matching <regexp2> is received.
      The syntax for <regexp1> and <regexp2> is the same as the regexp for notify.
      <timespec> is HH:MM[:SS]
      <command> is a usual fhem command like used in the at or notify

      Examples:
        # Request data from the FHT80 _once_ if we do not receive any message for
        # 15 Minutes.
        define w watchdog FHT80 00:15:00 SAME set FHT80 date
        # Request data from the FHT80 _each_ time we do not receive any message for
        # 15 Minutes, i.e. reactivate the watchdog after it triggered. Might be
        # dangerous, as it can trigger in a loop.
        define w watchdog FHT80 00:15:00 SAME set FHT80 date;; trigger w .
        # Shout once if the HMS100-FIT is not alive
        define w watchdog HMS100-FIT 01:00:00 SAME "alarm-fit.sh"
        # Send mail if the window is left open
        define w watchdog contact1:open 00:15 contact1:closed "mail_me close window1"
        attr w regexp1WontReactivate
      Notes:
      • if <regexp1> is . (dot), then activate the watchdog at definition time. Else it will be activated when the first matching event is received.
      • <regexp1> resets the timer of a running watchdog, to avoid it use the regexp1WontReactivate attribute.
      • if <regexp2> is SAME, then it will be the same as the first regexp, and it will be reactivated, when it is received.
      • 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)
      • a generic watchdog (one watchdog responsible for more devices) is currently not possible.
      • with modify all parameters are optional, and will not be changed if not specified.

    Set
      N/A

    Get
      N/A

    Attributes
    • disable
    • disabledForIntervals
    • regexp1WontReactivate
      When a watchdog is active, a second event matching regexp1 will normally reset the timeout. Set this attribute to prevents this.
    • execOnActivate If set, its value will be executed as a FHEM command when the watchdog is reactivated (after triggering) by receiving an event matching regexp1.
    • autoRestart 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.

=end html =begin html_DE

watchdog


    Define
      define <name> watchdog <regexp1> <timespec> <regexp2> <command>

      Startet einen beliebigen FHEM Befehl wenn nach dem Empfang des Ereignisses <regexp1> nicht innerhalb von <timespec> ein <regexp2> Ereignis empfangen wird.
      Der Syntax für <regexp1> und <regexp2> ist der gleiche wie regexp für notify.
      <timespec> ist HH:MM[:SS]
      <command> ist ein gewöhnlicher fhem Befehl wie z.B. in at oderr notify

      Beispiele:
        # Frage Daten vom FHT80 _einmalig_ ab, wenn wir keine Nachricht für
        # 15 Minuten erhalten haben.
        define w watchdog FHT80 00:15:00 SAME set FHT80 date

        # Frage Daten vom FHT80 jedes Mal ab, wenn keine Nachricht für
        # 15 Minuten emfpangen wurde, d.h. reaktiviere den Watchdog nachdem er getriggert wurde.
        # Kann gefährlich sein, da er so in einer Schleife getriggert werden kann.
        define w watchdog FHT80 00:15:00 SAME set FHT80 date;; trigger w .

        # Alarmiere einmalig wenn vom HMS100-FIT für eine Stunde keine Nachricht empfangen wurde.
        define w watchdog HMS100-FIT 01:00 SAME "alarm-fit.sh"

        # Sende eine Mail wenn das Fenster offen gelassen wurde
        define w watchdog contact1:open 00:15 contact1:closed "mail_me close window1"
        attr w regexp1WontReactivate

      Hinweise:
      • Wenn <regexp1> . (Punkt) ist, dann aktiviere den Watchdog zur definierten Zeit. Sonst wird er durch den Empfang des ersten passenden Events aktiviert.
      • <regexp1> Resetet den Timer eines laufenden Watchdogs. Um das zu verhindern wird das regexp1WontReactivate Attribut gesetzt.
      • Wenn <regexp2> SAME ist , dann ist es das gleiche wie das erste regexp, und wird reaktiviert wenn es empfangen wird.
      • 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.
        Der Watchdog musst immer mit diesem Befehl reaktiviert werden wenn er getriggert wurde.
      • Ein generischer Watchdog (ein Watchdog, verantwortlich für mehrere Devices) ist derzeit nicht möglich.
      • Bei modify sind alle Parameter optional, und werden nicht geaendert, falls nicht spezifiziert.

    Set
      N/A

    Get
      N/A

    Attribute
    • addStateEvent
    • disable
    • disabledForIntervals
    • regexp1WontReactivate
      Wenn ein Watchdog aktiv ist, wird ein zweites Ereignis das auf regexp1 passt normalerweise den Timer zurücksetzen. Dieses Attribut wird das verhindern.
    • execOnActivate Falls gesetzt, wird der Wert des Attributes als FHEM Befehl ausgeführt, wenn ein regexp1 Ereignis den Watchdog aktiviert nachdem er ausgelöst wurde.
    • autoRestart 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)

=end html_DE =cut