############################################## # $Id$ package main; use strict; use warnings; use vars qw($FW_ME); # webname (default is fhem) ##################################### sub notify_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "notify_Define"; $hash->{NotifyFn} = "notify_Exec"; $hash->{AttrFn} = "notify_Attr"; no warnings 'qw'; my @attrList = qw( addStateEvent:1,0 disable:1,0 disabledForIntervals disabledAfterTrigger forwardReturnValue:1,0 ignoreRegexp readLog:1,0 setList showtime:1,0 ); use warnings 'qw'; $hash->{AttrList} = join(" ", @attrList)." $readingFnAttributes"; $hash->{SetFn} = "notify_Set"; $hash->{StateFn} = "notify_State"; $hash->{FW_detailFn} = "notify_fhemwebFn"; } ##################################### sub notify_Define($$) { my ($hash, $def) = @_; my ($name, $type, $re, $command) = split("[ \t\n]+", $def, 4); if(!$command) { if($hash->{OLDDEF}) { # Called from modify, where command is optional (undef, $command) = split("[ \t]+", $hash->{OLDDEF}, 2); $hash->{DEF} = "$re $command"; } else { return "Usage: define notify "; } } # Checking for misleading regexps return "Bad regexp: starting with *" if($re =~ m/^\*/); eval { "Hallo" =~ m/^$re$/ }; return "Bad regexp in $name definition: $@" if($@); $hash->{REGEXP} = $re; my %specials= ( "%NAME" => $name, "%TYPE" => $name, "%EVENT" => "1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0", "%SELF" => $name, ); my $err = perlSyntaxCheck($command, %specials); return $err if($err); $hash->{".COMMAND"} = $command; my $doTrigger = ($name !~ m/^$re$/); # Forum #34516 readingsSingleUpdate($hash, "state", "active", $doTrigger) if(!$hash->{TEMPORARY}); InternalTimer(0, sub(){ notifyRegexpChanged($hash, $re); }, $hash); return undef; } ##################################### sub notify_Exec($$) { my ($ntfy, $dev) = @_; my $ln = $ntfy->{NAME}; return "" if(IsDisabled($ln)); my $now = gettimeofday(); my $dat = AttrVal($ln, "disabledAfterTrigger", 0); return "" if($ntfy->{TRIGGERTIME} && $now < $ntfy->{TRIGGERTIME}+$dat); my $n = $dev->{NAME}; my $re = $ntfy->{REGEXP}; my $iRe = AttrVal($ln, "ignoreRegexp", undef); my $events = deviceEvents($dev, AttrVal($ln, "addStateEvent", 0)); return if(!$events); # Some previous notify deleted the array. my $max = int(@{$events}); my $t = $dev->{TYPE}; my $ret = ""; for (my $i = 0; $i < $max; $i++) { my $s = $events->[$i]; $s = "" if(!defined($s)); my $found = ($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/s); if(!$found && AttrVal($n, "eventMap", undef)) { my @res = ReplaceEventMap($n, [$n,$s], 0); shift @res; $s = join(" ", @res); $found = ("$n:$s" =~ m/^$re$/); } if($found) { next if($iRe && ($n =~ m/^$iRe$/ || "$n:$s" =~ m/^$iRe$/)); Log3 $ln, 5, "Triggering $ln"; my %specials= ( "%NAME" => $n, "%TYPE" => $t, "%EVENT" => $s, "%SELF" => $ln ); my $exec = EvalSpecials($ntfy->{".COMMAND"}, %specials); Log3 $ln, 4, "$ln exec $exec"; my $r = AnalyzeCommandChain(undef, $exec); Log3 $ln, 3, "$ln return value: $r" if($r); $ret .= " $r" if($r); $ntfy->{TRIGGERTIME} = $now; setReadingsVal($ntfy, "triggeredByDev", $n, $dev->{NTFY_TRIGGERTIME}); setReadingsVal($ntfy, "triggeredByEvent", $s, $dev->{NTFY_TRIGGERTIME}); $ntfy->{STATE} = $dev->{NTFY_TRIGGERTIME} if(AttrVal($ln, 'showtime', !AttrVal($ln, "stateFormat", 0))); last if($dat); } } return $ret if(AttrVal($ln, "forwardReturnValue", 0)); return undef; } sub notify_Attr(@) { my @a = @_; my $do = 0; my $hash = $defs{$a[1]}; if($a[0] eq "set" && $a[2] eq "readLog") { if(!defined($a[3]) || $a[3]) { $logInform{$a[1]} = sub($$){ my ($me, $msg) = @_; return if(defined($hash->{CHANGED})); $hash->{CHANGED}[0] = $msg; notify_Exec($hash, $hash); delete($hash->{CHANGED}); } } else { delete $logInform{$a[1]}; } return; } if($a[0] eq "set" && $a[2] eq "ignoreRegexp") { return "Missing argument for ignoreRegexp" if(!defined($a[3])); eval { "HALLO" =~ m/$a[3]/ }; return $@; } if($a[0] eq "set" && $a[2] eq "disable") { $do = (!defined($a[3]) || $a[3]) ? 1 : 2; } $do = 2 if($a[0] eq "del" && (!$a[2] || $a[2] eq "disable")); return if(!$do); readingsSingleUpdate($hash, "state", $do==1 ? "disabled":"active", 1); return undef; } ################################### sub notify_Set($@) { my ($hash, @a) = @_; my $me = $hash->{NAME}; return "no set argument specified" if(int(@a) < 2); my %sets = (addRegexpPart=>2, removeRegexpPart=>1, inactive=>0, active=>0); my $setList = AttrVal($me, "setList", ""); my %usrSets = map { $_ =~ s/:.*//; $_ => 1 } split(" ", $setList); my $cmd = $a[1]; if(!defined($sets{$cmd}) && !defined($usrSets{$cmd})) { my $ret ="Unknown argument $cmd, choose one of ". join(" ", sort keys %sets)." $setList"; $ret =~ s/active/active:noArg/g; return $ret; } if($usrSets{$cmd}) { readingsSingleUpdate($hash, $cmd, join(" ",@a[2..$#a]), 1); return; } return "$cmd needs $sets{$cmd} parameter(s)" if(@a-$sets{$cmd} != 2); if($cmd eq "addRegexpPart") { my %h; my $re = "$a[2]:$a[3]"; map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP}); $h{$re} = 1; $re = join("|", sort keys %h); return "Bad regexp: starting with *" if($re =~ m/^\*/); eval { "Hallo" =~ m/^$re$/ }; return "Bad regexp: $@" if($@); $hash->{REGEXP} = $re; $hash->{DEF} = "$re ".$hash->{".COMMAND"}; notifyRegexpChanged($hash, $re); } elsif($cmd eq "removeRegexpPart") { my %h; map { $h{$_} = 1 } split(/\|/, $hash->{REGEXP}); return "Cannot remove regexp part: not found" if(!$h{$a[2]}); return "Cannot remove last regexp part" if(int(keys(%h)) == 1); delete $h{$a[2]}; my $re = join("|", sort keys %h); return "Bad regexp: starting with *" if($re =~ m/^\*/); eval { "Hallo" =~ m/^$re$/ }; return "Bad regexp: $@" if($@); $hash->{REGEXP} = $re; $hash->{DEF} = "$re ".$hash->{".COMMAND"}; notifyRegexpChanged($hash, $re); } elsif($cmd eq "inactive") { readingsSingleUpdate($hash, "state", "inactive", 1); } elsif($cmd eq "active") { readingsSingleUpdate($hash, "state", "active", 1) if(!AttrVal($me, "disable", undef)); } return undef; } ############# sub notify_State($$$$) { my ($hash, $tim, $vt, $val) = @_; $hash->{STATE} = $val if($vt eq "STATE" && !$init_done); return undef if($vt ne "state" || $val ne "inactive"); readingsSingleUpdate($hash, "state", "inactive", 1); return undef; } ######################### sub notify_fhemwebFn($$$$) { my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. my $hash = $defs{$d}; my $ret .= "
Change wizard". ""; my $row = 0; my @ra = split(/\|/, $hash->{REGEXP}); $ret .= ""; if(@ra > 1) { foreach my $r (@ra) { $ret .= ""; my $cmd = "cmd.X= set $d removeRegexpPart&val.X=$r"; # =.set: avoid JS $ret .= ""; $ret .= FW_pH("$cmd&detail=$d", "removeRegexpPart", 1,undef,1); $ret .= ""; } } $ret .= ''; my @et = devspec2array("TYPE=eventTypes"); if(!@et) { $ret .= ""; $ret .= FW_pH("$FW_ME/docs/commandref.html#eventTypes", "To add a regexp an eventTypes definition is needed", 1, undef, 1).""; } else { my %dh; my $etList = AnalyzeCommand(undef, "get $et[0] list"); $etList = "" if(!$etList); foreach my $l (split("\n", $etList)) { my @a = split(/[ \r\n]/, $l); $a[1] = "" if(!defined($a[1])); $a[1] =~ s/\.\*//g; $a[1] =~ s/,.*//g; next if(@a < 2); $dh{$a[0]}{".*"} = 1; $dh{$a[0]}{$a[1].".*"} = 1; } my $list = ""; foreach my $dev (sort keys %dh) { $list .= " $dev:" . join(",", sort keys %{$dh{$dev}}); } $list =~ s/(['"])/./g; $ret .= ""; $ret .= '"; } my ($tr, $js) = notfy_addFWCmd($d, $hash->{REGEXP}, $row); return "$ret$tr
Change the condition:
$r
'; $ret .= FW_detailSelect($d, "set", $list, "addRegexpPart"); $ret .= "

$js"; } sub notfy_addFWCmd($$$) { my ($d, $param, $row) = @_; my $ret=""; $ret .= "". " "; $ret .= "". "Change the executed command:"; $ret .= ""; my @list = grep { !$defs{$_}{TEMPORARY} && $_ ne $d && $modules{$defs{$_}{TYPE}}{SetFn} } sort keys %defs; $ret .= ""; $ret .= "
 $d $param set
"; $ret .= FW_select("modDev", "mod", \@list, undef, "set"); $ret .= ""; $ret .= ""; $ret .= ""; my $js = << 'END'; END return ($ret, $js); } 1; =pod =item helper =item summary execute a command upon receiving an event =item summary_DE führt bei Events Anweisungen aus =begin html

notify


    Define
      define <name> notify <pattern> <command>

      Execute a command when received an event for the definition <pattern>. If <command> is enclosed in {}, then it is a perl expression, if it is enclosed in "", then it is a shell command, else it is a "plain" fhem.pl command (chain). See the trigger command for testing it. Examples:
        define b3lampV1 notify btn3 set lamp $EVENT
        define b3lampV2 notify btn3 { fhem "set lamp $EVENT" }
        define b3lampV3 notify btn3 "/usr/local/bin/setlamp "$EVENT""
        define b3lampV3 notify btn3 set lamp1 $EVENT;;set lamp2 $EVENT
        define wzMessLg notify wz:measured.* "/usr/local/bin/logfht $NAME "$EVENT""
        define LogUndef notify global:UNDEFINED.* "send-me-mail.sh "$EVENT""

      Notes:
      • <pattern> is either the name of the triggering device, or devicename:event.
      • <pattern> must completely (!) match either the device name, or the compound of the device name and the event. To identify the events use the inform command from the telnet prompt or the "Event Monitor" link in the browser (FHEMWEB), and wait for the event to be printed. See also the eventTypes device.
      • in the command section you can access the event:
        • The variable $EVENT will contain the complete event, e.g. measured-temp: 21.7 (Celsius)
        • $EVTPART0,$EVTPART1,$EVTPART2,etc contain the space separated event parts (e.g. $EVTPART0="measured-temp:", $EVTPART1="21.7", $EVTPART2="(Celsius)". This data is available as a local variable in perl, as environment variable for shell scripts, and will be textually replaced for FHEM commands.
        • $NAME and $TYPE contain the name and type of the device triggering the event, e.g. myFht and FHT
        • $SELF contains the name of the notify itself
      • Note: the following is deprecated and will be removed in a future release. It is only active for featurelevel up to 5.6. The described replacement is attempted if none of the above variables ($NAME/$EVENT/etc) found in the command.
        • The character % will be replaced with the received event, e.g. with on or off or measured-temp: 21.7 (Celsius)
          It is advisable to put the % into double quotes, else the shell may get a syntax error.
        • The character @ will be replaced with the device name.
        • To use % or @ in the text itself, use the double mode (%% or @@).
        • Instead of % and @, the parameters %EVENT (same as %), %NAME (same as @) and %TYPE (contains the device type, e.g. FHT) can be used. The space separated event "parts" are available as %EVTPART0, %EVTPART1, etc. A single % looses its special meaning if any of these parameters appears in the definition.
      • Following special events will be generated for the device "global"
        • INITIALIZED after initialization is finished.
        • REREADCFG after the configuration is reread.
        • SAVE before the configuration is saved.
        • SHUTDOWN before FHEM is shut down.
        • DEFINED <devname> after a device is defined.
        • DELETED <devname> after a device was deleted.
        • RENAMED <old> <new> after a device was renamed.
        • UNDEFINED <defspec> upon reception of a message for an undefined device.
      • Notify can be used to store macros for manual execution. Use the trigger command to execute the macro. E.g.
        fhem> define MyMacro notify MyMacro { Log 1, "Hello"}
        fhem> trigger MyMacro

    Set
    • addRegexpPart <device> <regexp>
      add a regexp part, which is constructed as device:regexp. The parts are separated by |. Note: as the regexp parts are resorted, manually constructed regexps may become invalid.
    • removeRegexpPart <re>
      remove a regexp part. Note: as the regexp parts are resorted, manually constructed regexps may become invalid.
      The inconsistency in addRegexpPart/removeRegexPart arguments originates from the reusage of javascript functions.
    • inactive
      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.
      This command is intended to be used by scripts to temporarily deactivate the notify.
      The concurrent setting of the disable attribute is not recommended.
    • active
      Activates the current device (see inactive).

    Get
      N/A

    Attributes
    • disable
    • disabledForIntervals
    • disabledAfterTrigger someSeconds
      disable the execution for someSeconds after it triggered.
    • addStateEvent
      The event associated with the state Reading is special, as the "state: " string is stripped, i.e $EVENT is not "state: on" but just "on". In some circumstances it is desireable to get the event without "state: " stripped. In such a case the addStateEvent attribute should be set to 1 (default is 0, i.e. strip the "state: " string).
      Note 1: you have to set this attribute for the event "receiver", i.e. notify, FileLog, etc.
      Note 2: this attribute will only work for events generated by devices supporting the readingFnAttributes.
    • forwardReturnValue
      Forward the return value of the executed command to the caller, default is disabled (0). If enabled (1), then e.g. a set command which triggers this notify will also return this value. This can cause e.g FHEMWEB to display this value, when clicking "on" or "off", which is often not intended.
    • ignoreRegexp regexp
      It is hard to create a regexp which is _not_ matching something, this attribute helps in this case, as the event is ignored if it matches the argument. The syntax is the same as for the original regexp.
    • perlSyntaxCheck
    • readLog
      Execute the notify for messages appearing in the FHEM Log. The device in this case is set to the notify itself, e.g. checking for the startup message looks like:
        define n notify n:.*Server.started.* { Log 1, "Really" }
        attr n readLog
    • setList
      space separated list of user-defined commands. When executing such a command, a reading with the same name is set to the arguments of the command.
      Can be used in scenarios like:
        define Leuchtdauer notify schalter:on set Licht on-for-timer [$SELF:dauer]
        attr Leuchtdauer setList dauer:60,120,180

=end html =begin html_DE

notify


    Define
      define <name> notify <Suchmuster> <Anweisung>

      Führt eine oder mehrere Anweisungen aus, wenn ein Event generiert wurde, was dem <Suchmuster> (Gerätename oder Gerätename:Event) entspricht. Die Anweisung ist einer der FHEM Befehlstypen. Zum Test dient das trigger-Kommando.

      Beispiele:
        define b3lampV1 notify btn3 set lamp $EVENT
        define b3lampV2 notify btn3 { fhem "set lamp $EVENT" }
        define b3lampV3 notify btn3 "/usr/local/bin/setlamp "$EVENT""
        define b3lampV3 notify btn3 set lamp1 $EVENT;;set lamp2 $EVENT
        define wzMessLg notify wz:measured.* "/usr/local/bin/logfht $NAME "$EVENT""
        define LogUndef notify global:UNDEFINED.* "send-me-mail.sh "$EVENT""

      Hinweise:
      • <Suchmuster> ist entweder der Name des auslösenden ("triggernden") Gerätes oder die Kombination aus Gerät und auslösendem Ereignis (Event) Gerätename:Event.
      • Das <Suchmuster> muss exakt (!) entweder dem Gerätenamen entsprechen oder der Zusammenfügung aus Gerätename:Event. Events lassen sich mit "inform" in Telnet oder durch Beobachtung des "Event-Monitors" in FHEMWEB ermitteln.
      • In der Anweisung von Notify kann das auslösende Ereignis (Event) genutzt werden:
        • Die Anweisung $EVENT wird das komplette Ereignis (Event) beinhalten, z.B. measured-temp: 21.7 (Celsius)
        • $EVTPART0,$EVTPART1,$EVTPART2,etc enthalten die durch Leerzeichen getrennten Teile des Events der Reihe nach (im Beispiel also $EVTPART0="measured-temp:", $EVTPART1="21.7", $EVTPART2="(Celsius)".
          Diese Daten sind verfügbar als lokale Variablen in Perl, als Umgebungs-Variablen für Shell-Scripts, und werden als Text ausgetauscht in FHEM-Kommandos.
        • $NAME und $TYPE enthalten den Namen bzw. Typ des Ereignis auslösenden Gerätes, z.B. myFht und FHT
        • $SELF enthaelt den Namen dieser notify
      • Achtung: Folgende Vorgehensweise ist abgekündigt, funktioniert bis featurelevel 5.6 und wird in einem zukünftigen Release von FHEM nicht mehr unterstützt. Wenn keine der oben genannten Variablen ($NAME/$EVENT/usw.) in der Anweisung gefunden wird, werden Platzhalter ersetzt.
        • Das Zeichen % wird ersetzt mit dem empfangenen Ereignis (Event), z.B. mit on oder off oder measured-temp: 21.7 (Celsius).
        • Das Zeichen @ wird ersetzt durch den Gerätenamen.
        • Um % oder @ im Text selbst benutzen zu können, müssen sie verdoppelt werden (%% oder @@).
        • Anstelle von % und @, können die Parameter %EVENT (funktionsgleich mit %), %NAME (funktionsgleich mit @) und %TYPE (enthält den Typ des Gerätes, z.B. FHT) benutzt werden. Die von Leerzeichen unterbrochenen Teile eines Ereignisses (Event) sind verfügbar als %EVTPART0, %EVTPART1, usw. Ein einzeln stehendes % verliert seine oben beschriebene Bedeutung, falls auch nur einer dieser Parameter in der Definition auftaucht.
      • Folgende spezielle Ereignisse werden für das Gerät "global" erzeugt:
        • INITIALIZED sobald die Initialization vollständig ist.
        • REREADCFG nachdem die Konfiguration erneut eingelesen wurde.
        • SAVE bevor die Konfiguration gespeichert wird.
        • SHUTDOWN bevor FHEM heruntergefahren wird.
        • DEFINED <devname> nach dem Definieren eines Gerätes.
        • DELETED <devname> nach dem Löschen eines Gerätes.
        • RENAMED <old> <new> nach dem Umbenennen eines Gerätes.
        • UNDEFINED <defspec> beim Auftreten einer Nachricht für ein undefiniertes Gerät.
      • Notify kann dazu benutzt werden, um Makros für eine manuelle Ausführung zu speichern. Mit einem trigger Kommando können solche Makros dann ausgeführt werden. Z.B.
        fhem> define MyMacro notify MyMacro { Log 1, "Hello"}
        fhem> trigger MyMacro

    Set
    • addRegexpPart <device> <regexp>
      Fügt ein regexp Teil hinzu, der als device:regexp aufgebaut ist. Die Teile werden nach Regexp-Regeln mit | getrennt. Achtung: durch hinzufügen können manuell erzeugte Regexps ungültig werden.
    • removeRegexpPart <re>
      Entfernt ein regexp Teil. Die Inkonsistenz von addRegexpPart / removeRegexPart-Argumenten hat seinen Ursprung in der Wiederverwendung von Javascript-Funktionen.
    • inactive
      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.
      Der Einsatzzweck sind Skripte, um das notify temporär zu deaktivieren.
      Das gleichzeitige Verwenden des disable Attributes wird nicht empfohlen.
    • active
      Aktiviert das entsprechende Gerät, siehe inactive.

    Get
      N/A

    Attribute
    • disable
    • disabledForIntervals
    • disabledAfterTrigger <sekunden>
      deaktiviert die Ausführung für <sekunden> nach dem das notify ausgelöst wurde.
    • addStateEvent
      Das mit dem state Reading verknüpfte Event ist speziell, da das dazugehörige Prefix "state: " entfernt wird, d.h. $EVENT ist nicht "state: on", sondern nur "on". In manchen Fällen ist es aber erwünscht das unmodifizierte Event zu bekommen, d.h. wo "state: " nicht entfernt ist. Für diese Fälle sollte addStateEvent auf 1 gesetzt werden, die Voreinstellung ist 0 (deaktiviert).
      Achtung:
      • dieses Attribut muss beim Empfänger (notify, FileLog, etc) gesetzt werden.
      • dieses Attribut zeigt nur für solche Geräte-Events eine Wirkung, die readingFnAttributes unterstützen.
    • forwardReturnValue
      Rückgabe der Werte eines ausgeführten Kommandos an den Aufrufer. Die Voreinstellung ist 0 (ausgeschaltet), um weniger Meldungen im Log zu haben.
    • ignoreRegexp regexp
      Es ist nicht immer einfach ein Regexp zu bauen, was etwas _nicht_ matcht. Dieses Attribut hilft in diesen Fällen: das Event wird ignoriert, falls es den angegebenen Regexp matcht. Syntax ist gleich wie in der Definition.
    • perlSyntaxCheck
    • readLog
      Das notify wird für Meldungen, die im FHEM-Log erscheinen, ausgegeführt. Das "Event-Generierende-Gerät" wird auf dem notify selbst gesetzt. Z.Bsp. kann man mit folgendem notify auf die Startup Meldung reagieren:
        define n notify n:.*Server.started.* { Log 1, "Wirklich" }
        attr n readLog
    • setList
      Leerzeichen getrennte Liste von benutzerdefinierten Befehlen. Beim ausfüren solcher Befehle wird ein gleichnamiges Reading auf dem Wert des Befehls gesetzt. Kann z.Bsp. wie folgt verwendet werden:
        define Leuchtdauer notify schalter:on set Licht on-for-timer [$SELF:dauer]
        attr Leuchtdauer setList dauer:60,120,180

=end html_DE =cut