############################################## # $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"; } ##################################### # 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($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 || # trigger w . ($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); } 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 =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 int 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 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.

=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 FHT80 für 15 Minuten keine Nachricht # emfpangen wurde.
        define w watchdog HMS100-FIT 01:00: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 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.

=end html =cut