diff --git a/fhem/CHANGED b/fhem/CHANGED index a9cd86f13..3709d2c29 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - change: 98_monitoring: now packaged - change: 50_Signalbot: send message box, 0.10.5 group functions - bugfix: 32_withings: fixed sessionkey retrieval - bugfix: 70_SVDRP: handle timers with german Umlaute diff --git a/fhem/FHEM/98_monitoring.pm b/fhem/FHEM/98_monitoring.pm index de31c5913..f2a185537 100644 --- a/fhem/FHEM/98_monitoring.pm +++ b/fhem/FHEM/98_monitoring.pm @@ -1,6 +1,6 @@ -# Id ########################################################################## +########################################################################## # $Id$ - +# # copyright ################################################################### # # 98_monitoring.pm @@ -22,48 +22,88 @@ # FHEM. If not, see . # packages #################################################################### -package main; +package FHEM::Automation::monitoring; ##no critic qw(Package) use strict; use warnings; + use Carp qw(carp); + use Scalar::Util qw(looks_like_number); + use Time::HiRes qw(gettimeofday); + + use GPUtils qw(GP_Import); + +sub ::monitoring_Initialize { goto &Initialize } + + +BEGIN { + + GP_Import( qw( + readingsSingleUpdate + readingsBeginUpdate + readingsBulkUpdate + readingsEndUpdate + Log3 fhem + defs attr + DAYSECONDS HOURSECONDS MINUTESECONDS + init_done + InternalTimer + RemoveInternalTimer + readingFnAttributes + IsDisabled + AttrVal + InternalVal + ReadingsVal + ReadingsNum + devspec2array + AnalyzeCommandChain + AnalyzeCommand + EvalSpecials + AnalyzePerlCommand + perlSyntaxCheck + FmtDateTime time_str2num + notifyRegexpChanged + deviceEvents + ) ) +}; # initialize ################################################################## -sub monitoring_Initialize { - my $hash = shift // return; - my $TYPE = "monitoring"; +sub Initialize { + my $hash = shift // return; - $hash->{DefFn} = $TYPE."_Define"; - $hash->{UndefFn} = $TYPE."_Undefine"; - $hash->{SetFn} = $TYPE."_Set"; - $hash->{GetFn} = $TYPE."_Get"; - $hash->{AttrFn} = $TYPE."_Attr"; - $hash->{NotifyFn} = $TYPE."_Notify"; + $hash->{DefFn} = \&Define; + $hash->{UndefFn} = \&Undefine; + $hash->{DeleteFn} = \&Delete; + #$hash->{RenameFn} = \&Rename; + $hash->{SetFn} = \&Set; + $hash->{GetFn} = \&Get; + $hash->{AttrFn} = \&Attr; + $hash->{NotifyFn} = \&Notify; - $hash->{AttrList} = - "addStateEvent:1,0 ". - "blacklist:textField-long ". - "disable:1,0 ". - "disabledForIntervals ". - "errorFuncAdd:textField-long ". - "errorFuncAdded:textField-long ". - "errorFuncRemove:textField-long ". - "errorWait ". - "errorReturn:textField-long ". - "getDefault:all,error,warning ". - "setActiveFunc:textField-long ". - "setInactiveFunc:textField-long ". - "warningFuncAdd:textField-long ". - "warningFuncAdded:textField-long ". - "warningFuncRemove:textField-long ". - "warningWait ". - "warningReturn:textField-long ". - "whitelist:textField-long ". - $readingFnAttributes - ; + + $hash->{AttrList} = + "addStateEvent:1,0 ". + "blacklist:textField-long ". + "disable:1,0 ". + "disabledForIntervals ". + "errorFuncAdd:textField-long ". + "errorFuncAdded:textField-long ". + "errorFuncRemove:textField-long ". + "errorWait ". + "errorReturn:textField-long ". + "getDefault:all,error,warning ". + "setActiveFunc:textField-long ". + "setInactiveFunc:textField-long ". + "warningFuncAdd:textField-long ". + "warningFuncAdded:textField-long ". + "warningFuncRemove:textField-long ". + "warningWait ". + "warningReturn:textField-long ". + "whitelist:textField-long ". + $readingFnAttributes; return; } # regular Fn ################################################################## -sub monitoring_Define { +sub Define { my $hash = shift // return; my $def = shift // return; my ($SELF, $TYPE, @re) = split m{\s+}xms, $def, 5; @@ -72,21 +112,21 @@ sub monitoring_Define { if( !@re || @re > 2); monitoring_NOTIFYDEV($hash) if !$init_done; - monitoring_setActive($hash) if $init_done; + setActive($hash) if $init_done; return; } -sub monitoring_Undefine { +sub Undefine { my $hash = shift // return; - monitoring_setInactive($hash); + setInactive($hash); monitoring_RemoveInternalTimer($hash); return; } -sub monitoring_Set { +sub Set { my ($hash, @arr) = @_; my $TYPE = $hash->{TYPE}; @@ -112,10 +152,10 @@ sub monitoring_Set { if !exists $monitoring_sets{$argument}; if ( $argument eq 'active' ) { - return monitoring_setActive($hash); + return setActive($hash); } if ( $argument eq 'inactive' ) { - monitoring_setInactive($hash); + setInactive($hash); readingsSingleUpdate($hash, 'state', $argument, 0); Log3($SELF, 3, "$SELF ($TYPE) set $SELF inactive"); @@ -163,7 +203,7 @@ sub monitoring_Set { return; } -sub monitoring_Get { +sub Get { my ($hash, @arr) = @_; my $TYPE = $hash->{TYPE}; my $SELF = shift @arr; @@ -196,12 +236,12 @@ sub monitoring_Get { elsif($argument eq 'error' || $argument eq 'warning') { push @ret, monitoring_return($hash, $argument); } - - return join("\n\n", @ret)."\n" if @ret; + @ret = grep { defined } @ret; #prevent uninitialized warnings + return join ("\n\n", @ret)."\n" if @ret; return; } -sub monitoring_Attr { +sub Attr { my ($cmd, $SELF, $attribute, $value) = @_; my $hash = $defs{$SELF} // return; @@ -228,15 +268,15 @@ sub monitoring_Attr { for my $list ( qw(warning error) ){ for my $name ( split m{,}x, ReadingsVal($SELF, $list, '') ) { monitoring_modify("$SELF|$list|remove|$name") - if !grep {m/$name/} @whitelist; + if !grep {m{$name}x} @whitelist; } } } elsif($attribute eq 'disable'){ if($cmd eq 'set' and $value == 1){ - return monitoring_setActive($hash); + return setActive($hash); } - monitoring_setInactive($hash); + setInactive($hash); readingsSingleUpdate($hash, 'state', 'disabled', 0); Log3($SELF, 3, "$hash->{TYPE} ($SELF) attr $SELF disabled"); } @@ -244,7 +284,7 @@ sub monitoring_Attr { return; } -sub monitoring_Notify { +sub Notify { my $hash = shift // return; my $dev_hash = shift // return; my $SELF = $hash->{NAME}; @@ -262,8 +302,8 @@ sub monitoring_Notify { return if !$events; - if($name eq 'global' && 'INITIALIZED|REREADCFG' =~ m/\Q@{$events}\E/){ - monitoring_setActive($hash); + if($name eq 'global' && 'INITIALIZED|REREADCFG' =~ m{\Q@{$events}\E}x){ + setActive($hash); return; } @@ -283,32 +323,43 @@ sub monitoring_Notify { push @blacklist, devspec2array($_) for (split m{[\s]+}x, AttrVal($SELF, 'blacklist', '')); - return if @blacklist && grep {/$name/} @blacklist; + return if @blacklist && grep { m{$name}x } @blacklist; my @whitelist; push @whitelist, devspec2array($_) for (split m{[\s]+}x, AttrVal($SELF, 'whitelist', '')); - return if @whitelist && !grep {/$name/} @whitelist; + return if @whitelist && !grep { m{$name}x } @whitelist; for my $event (@{$events}){ next if !$event; my $addMatch = "$name:$event" =~ m{\A$addRegex\z}xms; my $removeMatch = $removeRegex ? "$name:$event" =~ m{\A$removeRegex\z}xms : 0; - #Log3($hash, 3, "monitoring_notify called with add $addMatch and remove $removeMatch"); + #Log3($hash, 3, "notify called with add $addMatch and remove $removeMatch"); #next unless(defined($event) && ($addMatch || $removeMatch)); next if !$addMatch && !$removeMatch; - #Log3($hash, 3, "monitoring_notify unless 1 replacement passed w. $addMatch and remove $removeMatch"); + #Log3($hash, 3, "notify unless 1 replacement passed w. $addMatch and remove $removeMatch"); Log3($SELF, 4 , "$TYPE ($SELF) triggered by \"$name $event\""); for my $list ( qw (error warning) ){ my $listFuncAdd = AttrVal($SELF, $list.'FuncAdd', 'preset'); my $listFuncRemove = AttrVal($SELF, $list.'FuncRemove', 'preset'); - my $listWait = eval(AttrVal($SELF, $list.'Wait', 0)); + #my $listWait = eval(AttrVal($SELF, $list.'Wait', 0)); + + my $cmd = AttrVal($SELF, $list.'Wait', 0); + my %specials = ( + '$name' => $name, #Name des Event auslösenden Gerätes + '$SELF' => $SELF, #Eigenname des monitoring + ); + + $cmd = EvalSpecials($cmd, %specials); + + # CMD ausführen + my $listWait = AnalyzePerlCommand( $hash, $cmd ); $listWait = 0 if !looks_like_number($listWait); if ( $listFuncAdd eq 'preset' && $listFuncRemove eq 'preset' ) { @@ -378,9 +429,17 @@ sub monitoring_Notify { $listFuncRemove = 1 if $listFuncRemove eq 'preset' && $removeMatch; } - $listFuncAdd = eval($listFuncAdd) if $listFuncAdd =~ /^\{.*\}$/s; - $listFuncRemove = eval($listFuncRemove) - if($listFuncRemove =~ /^\{.*\}$/s); + #$listFuncAdd = eval($listFuncAdd) if $listFuncAdd =~ /^\{.*\}$/s; + if ( $listFuncAdd =~ m{\A\{.*\}\z}xs ) { + $listFuncAdd = EvalSpecials($listFuncAdd, %specials); + + # CMD ausführen + $listFuncAdd = AnalyzePerlCommand( $hash, $listFuncAdd) + }; + if($listFuncRemove =~ m{\A\{.*\}\z}xs ) { + $listFuncRemove = EvalSpecials($listFuncRemove, %specials); + $listFuncRemove = AnalyzePerlCommand($hash, $listFuncRemove) + } monitoring_modify("$SELF|$list|remove|$name") if $listFuncRemove && $listFuncRemove eq '1'; @@ -402,7 +461,11 @@ sub monitoring_modify { return if IsDisabled($SELF); my $at; - $at = eval($wait + gettimeofday()) if $wait && $wait ne 'quiet'; + #$at = eval($wait + gettimeofday()) if $wait && $wait ne 'quiet'; + if ( $wait && $wait ne 'quiet' ) { + $at = looks_like_number($wait) ? $wait : AnalyzeCommandChain($hash, $wait); + $at +=gettimeofday(); + } my $TYPE = $hash->{TYPE}; my (@change, %readings); %readings = map{ $_ => 1 } split m{,}xms, ReadingsVal($SELF, $list, ''); @@ -421,14 +484,14 @@ sub monitoring_modify { if ( $operation eq 'add' ) { return if $readings{$value} || - ReadingsVal($SELF, 'error', '') =~ m/(?:^|,)$value(?:,|$)/ + ReadingsVal($SELF, 'error', '') =~ m{(?:^|,)$value(?:,|$)}x ; if ( $at ){ return if $hash->{READINGS}{$reading}; readingsSingleUpdate($hash, $reading, FmtDateTime($at), 0); - InternalTimer($at, 'monitoring_modify', $arg); + InternalTimer($at, \&monitoring_modify, $arg); return; } @@ -455,7 +518,13 @@ sub monitoring_modify { if ($operation eq 'add') { my $name = $value; my $listFuncAdded = AttrVal($SELF, $list.'FuncAdded', ''); - $listFuncAdded = $listFuncAdded =~ /^\{.*\}$/s ? eval($listFuncAdded) : fhem($listFuncAdded); + my %specials = ( + '$name' => $name, #Name des Event auslösenden Gerätes + '$SELF' => $SELF, #Eigenname des monitoring + ); + $listFuncAdded = EvalSpecials($listFuncAdded, %specials); + $listFuncAdded = AnalyzeCommandChain($hash, $listFuncAdded); + #$listFuncAdded = $listFuncAdded =~ /^\{.*\}$/s ? eval($listFuncAdded) : fhem($listFuncAdded); } readingsBeginUpdate($hash); @@ -506,10 +575,15 @@ sub monitoring_return { $ret = '"$list: $value"' if !$ret && $value; return if !$ret; - return eval($ret); + #return ; + + if ( !eval{ $ret = eval($ret) ; 1 } ) { ##no critic qw(StringyEval) + return Log3($hash->{NAME}, 1, "Evaluation error: $@"); + } + return $ret; } -sub monitoring_setActive { +sub setActive { my $hash = shift // return; my $SELF = $hash->{NAME} // return; my $TYPE = $hash->{TYPE}; @@ -542,7 +616,7 @@ sub monitoring_setActive { return; } -sub monitoring_setInactive { +sub setInactive { my $hash = shift // return; my $SELF = $hash->{NAME} // return; my $TYPE = $hash->{TYPE}; @@ -827,7 +901,8 @@ __END__ With this attribute the output created with "get <name> error" - can be formatted. + can be formatted.
+ Note: As evaluation is done in package context, using functions from main may require redirection in lexical main context (e.g. use double colons "::myFunction($SELF)").
  • getDefault (all|error|warning)
    @@ -1332,7 +1407,8 @@ attr BeamerFilter_monitoring warningFuncRemove {return}
  • Mit diesem Attribut kann die Ausgabe die mit "get <name> error" - erzeugt wird angepasst werden. + erzeugt wird angepasst werden.
    + Hinweis: Da der Code im package-Kontext evaluiert wird, muss ggf. bei Funktionen aus dem main Kontext explizit ein Verweis auf diesen Kontext ergänzt werden(z.B. mit vorangestelltem doppeltem Doppelpunt "::myFunction($SELF)").
  • getDefault (all|error|warning)
    diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 0cc41ad53..5a41d928e 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -536,7 +536,7 @@ FHEM/98_ModbusAttr.pm StefanStrobel Sonstiges FHEM/98_ModbusElsnerWS.pm klaus.schauer Sonstiges FHEM/98_ModbusSET.pm StefanStrobel Sonstiges FHEM/98_ModbusTrovis5576.pm Reinerlein Heizungssteuerung/Raumklima -FHEM/98_monitoring.pm Beta-User Automatisierung +FHEM/98_monitoring.pm Beta-User Automatisierung https://forum.fhem.de/index.php/topic,126515.0.html FHEM/98_notice.pm mfr69bs Sonstiges FHEM/98_PHC.pm StefanStrobel Sonstiges FHEM/98_PID20.pm John Automatisierung