2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 23:06:37 +00:00

98_readingsWatcher: fix for perlcritic and PBP

git-svn-id: https://svn.fhem.de/fhem/trunk@21606 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Wzut 2020-04-05 17:27:05 +00:00
parent f265bd3903
commit 33ea36813b

View File

@ -21,43 +21,15 @@
# GNU General Public License for more details.
################################################################
package main;
package FHEM::readingsWatcher; ## no critic 'package'
use strict;
use warnings;
eval "use FHEM::Meta;1";
#####################################################################################
sub readingsWatcher_Initialize($)
{
my ($hash) = @_;
$hash->{GetFn} = "FHEM::readingsWatcher::Get";
$hash->{SetFn} = "FHEM::readingsWatcher::Set";
$hash->{DefFn} = "FHEM::readingsWatcher::Define";
$hash->{UndefFn} = "FHEM::readingsWatcher::Undefine";
$hash->{AttrFn} = "FHEM::readingsWatcher::Attr";
$hash->{AttrList} = "disable:0,1 interval deleteUnusedReadings:1,0 readingActivity ".$readingFnAttributes;
eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html)
return;
}
###############################################################
# Begin Package
###############################################################
package FHEM::readingsWatcher;
use strict;
use warnings;
use GPUtils qw(:all); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
use POSIX;
use utf8;
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
use Time::HiRes qw(gettimeofday);
eval "use FHEM::Meta;1" or my $modMetaAbsent = 1;
# Run before module compilation
BEGIN {
BEGIN
{
# Import from main::
GP_Import(
qw(
@ -75,13 +47,14 @@ BEGIN {
RemoveInternalTimer
IsDisabled
IsIgnored
Log3
modules
Log3
modules
readingsSingleUpdate
readingsBulkUpdate
readingsBeginUpdate
readingsDelete
readingsEndUpdate
readingFnAttributes
ReadingsNum
ReadingsAge
ReadingsTimestamp
@ -93,12 +66,20 @@ BEGIN {
TimeNow
)
);
# Export to main context
GP_Export(
qw(
Initialize
)
);
}
# Versions History intern
our %vNotesIntern =
my %vNotesIntern =
(
"1.7.1" => "25.01.20 fix ErrorValue 0",
"2.0.0" => "April 2020 , perlcritic -4 / PBP",
"1.7.1" => "25.01.20 fix ErrorValue 0",
"1.7.0" => "12.01.20 add OR / AND watching",
"1.6.0" => "27.08.19 package, Meta",
"1.5.0" => "18.02.19",
@ -108,21 +89,44 @@ our %vNotesIntern =
"1.0.0" => "(c) HCS, first version"
);
our %gets = ("devices:noArg" => "");
our %sets = ("checkNow:noArg" => "","inactive:noArg"=>"", "active:noArg"=>"" , "clearReadings:noArg"=>"");
my $hasmeta = 0;
if (-e $attr{global}{modpath}.'/FHEM/Meta.pm')
{
$hasmeta = 1;
require FHEM::Meta;
}
sub Initialize
{
my $hash = shift;
$hash->{GetFn} = "FHEM::readingsWatcher::Get";
$hash->{SetFn} = "FHEM::readingsWatcher::Set";
$hash->{DefFn} = "FHEM::readingsWatcher::Define";
$hash->{UndefFn} = "FHEM::readingsWatcher::Undefine";
$hash->{AttrFn} = "FHEM::readingsWatcher::Attr";
$hash->{AttrList} = "disable:0,1 interval deleteUnusedReadings:1,0 readingActivity ".$readingFnAttributes;
return FHEM::Meta::InitMod( __FILE__, $hash ) if ($hasmeta);
return;
}
#####################################################################################
sub Define($$)
sub Define
{
my ($hash,$def) = @_;
my $hash = shift;
my $def = shift;
my ($name, $type, $noglobal) = split("[ \t\n]+", $def, 3);
if(exists($modules{readingsWatcher}{defptr}))
{
my $txt = 'one readingsWatcher device is already defined !';
Log3 $name, 1, $txt;
return $txt;
my $error = 'one readingsWatcher device is already defined !';
Log3 $name, 1, $error;
return $error;
}
$modules{readingsWatcher}{defptr} = $hash;
@ -131,20 +135,21 @@ sub Define($$)
{ $hash->{DEF} = 'noglobal'; }
else { addToAttrList('readingsWatcher'); $hash->{DEF} = 'global';} # global -> userattr
CommandAttr(undef,$name.' interval 60') unless (exists($attr{$name}{interval}));
CommandAttr(undef,$name.' readingActivity none') unless (exists($attr{$name}{readingActivity}));
CommandAttr(undef,"$name interval 60") unless (exists($attr{$name}{interval}));
CommandAttr(undef,"$name readingActivity none") unless (exists($attr{$name}{readingActivity}));
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+5, "FHEM::readingsWatcher::OnTimer", $hash, 0);
if (!$modMetaAbsent) {return $@ unless ( FHEM::Meta::SetInternals($hash) ) }
return undef;
InternalTimer(gettimeofday()+5, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
if ($hasmeta) { return $@ unless ( FHEM::Meta::SetInternals($hash) ) }
return;
}
#####################################################################################
sub Undefine($$)
sub Undefine
{
my ($hash, $arg) = @_;
my $hash = shift;
RemoveInternalTimer($hash);
delete($modules{readingsWatcher}{defptr});
if ($hash->{DEF} eq 'global')
@ -154,65 +159,68 @@ sub Undefine($$)
foreach (@devs)
{ delFromDevAttrList($_, 'readingsWatcher'); } # aufräumen
}
return undef;
return;
}
#####################################################################################
sub Set($@)
sub Set
{
my ($hash, @a)= @_;
my $name= $hash->{NAME};
return join(' ', sort keys %sets) if((@a < 2) || ($a[1] eq '?'));
if ($a[1] eq 'inactive')
my ($hash, $name, $cmd) = @_;
if ($cmd eq 'inactive')
{
readingsSingleUpdate($hash, 'state', 'inactive', 1);
RemoveInternalTimer($hash);
$hash->{INTERVAL}=0;
return undef;
$hash->{INTERVAL} = 0;
return;
}
elsif ($a[1] eq 'active')
elsif ($cmd eq 'active')
{
readingsSingleUpdate($hash, 'state', 'active', 1);
$hash->{INTERVAL} = AttrVal($name,'interval',60);
return;
}
return undef if(IsDisabled($name));
return if(IsDisabled($name));
OnTimer($hash) if (($a[1] eq 'checkNow') || ($a[1] eq 'active'));
if (($cmd eq 'checkNow') || ($cmd eq 'active'))
{
OnTimer($hash);
return;
}
if ($a[1] eq 'clearReadings')
if ($cmd eq 'clearReadings')
{
foreach (keys %{$defs{$name}{READINGS}}) # alle eignen Readings
{
if ($_ =~ /_/) # device.reading
{
readingsDelete($hash, $_);
Log3 $name,4,$name.", delete reading ".$_;
Log3 $name,4,"$name, delete reading $_";
}
}
return;
}
return undef;
return "unknown argument $cmd, choose one of checkNow:noArg inactive:noArg active:noArg clearReadings:noArg";
}
#####################################################################################
sub Get($@)
sub Get
{
my ($hash, @a)= @_;
my (@parts, $deviceName, $rSA, $d, $curVal, $age , @devs, $state);
my ($hash, $name , $cmd)= @_;
return if(!$cmd);
return join(' ', sort keys %gets) if(@a < 2);
my $name = $hash->{NAME};
my (@parts, $rSA, $age , @devs, $state);
my ($dw,$rw,$tw,$sw,$aw) = (6,7,7,5,3);
if ($a[1] eq 'devices')
if ($cmd eq 'devices')
{
foreach $deviceName (devspec2array("readingsWatcher!="))
foreach my $deviceName (devspec2array("readingsWatcher!="))
{
$rSA = ($deviceName eq $a[0]) ? '' : AttrVal($deviceName, 'readingsWatcher', '');
$rSA = ($deviceName eq $name) ? '' : AttrVal($deviceName, 'readingsWatcher', '');
$dw = length($deviceName) if (length($deviceName) > $dw);
if ($rSA)
@ -235,7 +243,7 @@ sub Get($@)
{
$_ =~ s/\+/,/g;
@parts = split(',', $_);
if (@parts > 2)
if (@parts > 2)
{
my $timeout = int($parts[0]);
$tw = length($timeout) if(length($timeout) > $tw);
@ -270,7 +278,7 @@ sub Get($@)
}
$aw = length($age) if(length($age) > $aw);
$sw = length($state) if(length($state) > $sw);
push @devs, "$deviceName,".$_.",$timeout,$state,$age";
push @devs, "$deviceName, $_, $timeout, $state, $age";
}
} # @parts >2
@ -293,31 +301,32 @@ sub Get($@)
my $s = 'Device'.(' ' x ($dw-6)).'| Reading'.(' ' x ($rw-7)).'|'.(' ' x ($tw-6)).'Timeout |'.(' ' x ($sw-5)).'State |'.(' ' x ($aw-3)).'Age';
my $line = ('-'x(length($s)));
while ( $s =~ m/\|/g ) { substr($line,(pos($s)-1),1) = '+'; }
while ( $s =~ m/\|/g ) { substr $line,(pos($s)-1),1,'+'; }
$s .= "\n".$line."\n";
foreach(@devs)
{
@a = split(',',$_);
$s .= $a[0] . (' ' x ($dw - length$a[0])); # linksbündig
$s .= '| '.$a[1] . (' ' x ($rw - length$a[1])); # linksbündig
$s .= '| '.(' ' x ($tw - length$a[2])).$a[2]; # rechtsbündig
$s .= ' |'.(' ' x ($sw - length$a[3])).$a[3].' |'; # rechtsbündig
$s .= (' ' x ($aw - length$a[4])).$a[4]; # rechtsbündig
my @ar = split(',',$_);
$s .= $ar[0] . (' ' x ($dw - length$ar[0])); # linksbündig
$s .= '| '.$ar[1] . (' ' x ($rw - length$ar[1])); # linksbündig
$s .= '| '.(' ' x ($tw - length$ar[2])).$ar[2]; # rechtsbündig
$s .= ' |'.(' ' x ($sw - length$ar[3])).$ar[3].' |'; # rechtsbündig
$s .= (' ' x ($aw - length$ar[4])).$ar[4]; # rechtsbündig
$s .= "\n";
}
return $s.$line;
}
return 'Sorry, no devices with valid attribute readingsWatcher found !';
} # get devices
return 'get '.$name.' with unknown argument '.$a[1].', choose one of ' . join(' ', sort keys %gets);
return "unknown command $cmd, choose one of devices:noArg";
}
#####################################################################################
sub OnTimer($)
sub OnTimer
{
my ($hash) = @_;
my $hash = shift;
my $name = $hash->{NAME};
my $interval = AttrNum($name, 'interval', 0);
$hash->{INTERVAL} = $interval;
@ -330,8 +339,8 @@ sub OnTimer($)
readingsSingleUpdate($hash, 'state', 'disabled', 0) if (IsDisabled($name));
return if(IsDisabled($name) || !$init_done );
my ($timeOutState, $errorValue, $timeout, $associated, $error, $readingsList);
my ($deviceName, $rSA, $age, @devices, $rts, @parts, @devs);
my ($timeOutState, $errorValue, $timeout, $error, $readingsList);
my ($rSA, $age, @devices, $rts, @parts, @devs);
my ($alives, $deads, $state, $readings, $skipped, $timeouts) = (0, 0, '', 0, 0, 0);
my @timeOutdevs = (); my @deadDevs = (); my @skipDevs = ();
@ -347,15 +356,15 @@ sub OnTimer($)
readingsBeginUpdate($hash);
foreach $deviceName (@devs)
foreach my $deviceName (@devs)
{
my $or_and = 0; # Readings beim auswerten OR
my ($d_a,$d_d,$ok_device) = (0,0,0);
$timeOutState = '';
$rSA = ($deviceName eq $name) ? '' : AttrVal($deviceName, 'readingsWatcher', undef);
$rSA = ($deviceName eq $name) ? '' : AttrVal($deviceName, 'readingsWatcher', '');
if(defined($rSA) && !IsDisabled($deviceName) && !IsIgnored($deviceName))
if($rSA && !IsDisabled($deviceName) && !IsIgnored($deviceName))
{
push @devices, $deviceName if !grep {/$deviceName/} @devices; # keine doppelten Namen
@ -419,7 +428,7 @@ sub OnTimer($)
if ($timeout < 1)
{
$error = 'Invalid timeout value '.$timeout.' for reading '.$deviceName.'.'.$_;
$error = "Invalid timeout value $timeout for reading $deviceName $_";
Log3 $name,2,$name.', '.$error;
}
}#age
@ -453,31 +462,31 @@ sub OnTimer($)
{
Log3 $name,2,$name.', insufficient parameters for device '.$deviceName.' - skipped !';
$skipped++;
CommandSetReading(undef, "$deviceName $areading unkown") if ($areading);
CommandSetReading(undef, "$deviceName $areading unknown") if ($areading);
push @skipDevs, $deviceName;
}
} # defined($rSA) && !IsDisabled($deviceName) && !IsIgnored($deviceName)
} # foreach $deviceName
readingsBulkUpdate($hash,'readings' , $readings);
readingsBulkUpdate($hash,'devices' , int(@devices));
readingsBulkUpdate($hash,'alive' , $alives);
readingsBulkUpdate($hash,'dead' , $deads);
readingsBulkUpdate($hash,'skipped' , $skipped) if ($skipped);
readingsBulkUpdate($hash,'timeouts' , $timeouts);
readingsBulkUpdate($hash,'state' , ($timeouts) ? 'timeout' : 'ok');
readingsBulkUpdate($hash,'readings' , $readings);
readingsBulkUpdate($hash,'devices' , int(@devices));
readingsBulkUpdate($hash,'alive' , $alives);
readingsBulkUpdate($hash,'dead' , $deads);
readingsBulkUpdate($hash,'skipped' , $skipped) if ($skipped);
readingsBulkUpdate($hash,'timeouts' , $timeouts);
readingsBulkUpdate($hash,'state' , ($timeouts) ? 'timeout' : 'ok');
readingsDelete($hash, 'skipped') if (!$skipped && AttrNum($name,'deleteUnusedReadings','1'));
# nicht aktualisierte Readings markieren oder löschen
if ($readingsList)
{
my @a = split(",",$readingsList);
foreach (@a)
my @ar = split(",",$readingsList);
foreach (@ar)
{
if ($_)
{
if (AttrNum($name,'deleteUnusedReadings','1'))
if (AttrNum($name,'deleteUnusedReadings',1))
{
readingsDelete($hash, $_);
Log3 $name,3,$name.', delete unused reading '.$_;
@ -491,7 +500,7 @@ sub OnTimer($)
}
}
if (int(@devices))
if (@devices)
{ readingsBulkUpdate($hash,'.associatedWith' , join(',',@devices)); }
else
{ readingsDelete($hash, '.associatedWith'); }
@ -513,30 +522,29 @@ sub OnTimer($)
readingsEndUpdate($hash, 1);
return undef;
return;
}
sub Attr (@)
sub Attr
{
my ($cmd, $name, $attrName, $attrVal) = @_;
my $hash = $defs{$name};
my $error;
return 'not allowed !' if ($name eq $attrName);
if ($cmd eq 'set')
{
if ($attrName eq 'disable')
{
readingsSingleUpdate($hash,'state','disabled',1) if ($attrVal == 1);
OnTimer($hash) if ($attrVal == 0);
$_[3] = $attrVal;
readingsSingleUpdate($hash,'state','disabled',1) if (int($attrVal) == 1);
OnTimer($hash) if (int($attrVal) == 0);
return;
}
if (($attrName eq 'readingActivity') && ($attrVal eq 'state'))
{
$_[3] = '';
my $err = 'forbidden value state !';
Log3 $name,1,$name.', readingActivity '.$err;
return $err;
my $error = 'forbidden value state !';
Log3 $name,1,"$name, readingActivity $error";
return $error;
}
}
elsif ($cmd eq 'del')
@ -546,12 +554,14 @@ sub Attr (@)
OnTimer($hash);
}
}
return undef;
return;
}
#####################################################################################
1;
=pod
=encoding utf8
=item helper
=item summary cyclical watching of readings updates
=item summary_DE zyklische Überwachung von Readings auf Aktualisierung
@ -630,8 +640,8 @@ sub Attr (@)
<a name="readingsWatcher"></a>
<h3>readingsWatcher</h3>
<ul>
Das Modul &uuml;berwacht Readings in anderen Modulen darauf das sich dessen Readings bzw. deren Zeiten in bestimmten Abst&auml;nden
ändern<br> und l&ouml;st ggf. Events aus die mit anderen Modulen (z.B. notify,DOIF) weiter verarbeitet werden k&ouml;nnen.<br>
Das Modul überwacht Readings in anderen Modulen darauf das sich dessen Readings bzw. deren Zeiten in bestimmten Abständen
ändern<br> und löst ggf. Events aus die mit anderen Modulen (z.B. notify,DOIF) weiter verarbeitet werden können.<br>
Forum : <a href="https://forum.fhem.de/index.php/topic,49408.0.html">https://forum.fhem.de/index.php/topic,49408.0.html</a><br><br>
<a name="readingsWatcher_Define"></a>
@ -641,18 +651,18 @@ sub Attr (@)
<br>
Definiert ein readingsWatcher Device.<br><br>
Danach besitzt jedes FHEM Device das neue globale Attribut readingsWatcher<br>
Dieses Attribut ist bei allen zu &uuml;berwachenden Ger&auml;ten wie folgt zu belegen :<br>
Dieses Attribut ist bei allen zu überwachenden Geräten wie folgt zu belegen :<br>
<code>attr mydevice timeout,[Ersatz],Readings1,[Readings2][;timeout2,Ersatz2,Reading,Reading]</code>
Timeout in Sekunden, neuer Reading Wert, Reading1 des Devices, Reading2, usw.<br><br>
Beispiel : Ein Funk Thermometer sendet in regelm&auml;&szlig;igen Abst&auml;nden ( z.b.5 Sekunden ) seine Werte.<br>
Beispiel : Ein Funk Thermometer sendet in regelmäßigen Abständen ( z.b.5 Sekunden ) seine Werte.<br>
Bleiben diese nun für eine bestimmte Zeit aus, so kann das Modul diesen nun nicht mehr aktuellen Werte (oder Werte)<br>
mit einem beliebigen anderen Wert &uuml;berschreiben ( z.B. ??? )<br>
Das Attribut readingsWatcher k&ouml;nnte hier wie folgt gesetzt werden :<br><br>
mit einem beliebigen anderen Wert überschreiben ( z.B. ??? )<br>
Das Attribut readingsWatcher könnte hier wie folgt gesetzt werden :<br><br>
<code>attr AussenTemp readingsWatcher 300,???,temperature</code><br><br>
oder falls mehr als ein Reading &uuml;berwacht werden soll<br><br>
<code>attr AussenTemp readingsWatcher 300,???,temperature,humidity</code><br><br>
Sollen Readings nur auf ihre Aktualiesierung &uuml;berwacht, deren Wert aber <b>nicht</b> &uuml;berschrieben werden,<br>
so mu&szlig; der Ersatzsstring <b>leer</b> gelassen werden :<br>
Sollen Readings nur auf ihre Aktualiesierung überwacht, deren Wert aber <b>nicht</b> überschrieben werden,<br>
so muss der Ersatzsstring <b>leer</b> gelassen werden :<br>
Bsp : <code>attr AussenTemp readingsWatcher 300,,temperature,humidity</code><br><br>
<br>
weitere Beispiele :<br>
@ -681,15 +691,15 @@ sub Attr (@)
<br>
<ul>
<a name="disable"></a><li><b>disable</b><br>Deaktiviert das Device</li><br>
<a name="interval"></a><li><b>interval &lt;Sekunden&gt;</b> (default 60)<br>Zeitintervall zur kontinuierlichen &Uuml;berpr&uuml;fung</li><br>
<a name="deleteUnusedReadings"></a><li><b>deleteUnusedReadings</b> (default 1)<br>Readings mit dem Wert unused werden automatisch gel&ouml;scht</li><br>
<a name="interval"></a><li><b>interval &lt;Sekunden&gt;</b> (default 60)<br>Zeitintervall zur kontinuierlichen Überprüfung</li><br>
<a name="deleteUnusedReadings"></a><li><b>deleteUnusedReadings</b> (default 1)<br>Readings mit dem Wert unused werden automatisch gelöscht</li><br>
<a name="readingActifity"></a><li><b>readingActifity</b> (default none)<br>
Das Modul kann &auml;hnlich dem HomeMatic ActionDetector im &uuml;berwachten Gerät ein eigenes Reading setzen und den &Uuml;berwachungsstatus<br>
Das Modul kann ähnlich dem HomeMatic ActionDetector im überwachten Gerät ein eigenes Reading setzen und den Überwachungsstatus<br>
in diesem speichern. Beispiel :<br>
<code>attr &lt;name&gt; readingActifity actifity</code><br>
Erzeugt in den &uuml;berwachten Ger&auml;ten das zus&auml;tzliche Reading actifity und versorgt es mit dem Status dead bzw alive<br>
Erzeugt in den überwachten Geräten das zusäzliche Reading actifity und versorgt es mit dem Status dead bzw alive<br>
<code>attr &lt;name&gt; readingActifity aktiv:0:1</code><br>
Erzeugt in den &uuml;berwachten Ger&auml;ten das zus&auml;tzliche Reading aktiv und versorgt es mit dem Status 0 bzw 1
Erzeugt in den überwachten Geräten das zusätzliche Reading aktiv und versorgt es mit dem Status 0 bzw 1
</li><br>
</ul>
</ul>
@ -699,7 +709,6 @@ sub Attr (@)
=end html_DE
=encoding utf8
=for :application/json;q=META.json 98_readingsWatcher.pm
{
@ -713,9 +722,9 @@ sub Attr (@)
"readings",
"watch",
"supervision",
"ueberwachung"
"überwachung"
],
"version": "1.6.0",
"version": "2.0.0",
"release_status": "stable",
"author": [
"Wzut"
@ -731,7 +740,6 @@ sub Attr (@)
"requires": {
"FHEM": 5.00918799,
"perl": 5.014,
"POSIX": 0,
"GPUtils": 0,
"Time::HiRes": 0
},