98_readingsWatcher: refactoring

git-svn-id: https://svn.fhem.de/fhem/trunk@21629 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Wzut 2020-04-08 17:57:23 +00:00
parent 50d81b8beb
commit ec7f5baf7f

View File

@ -1,6 +1,8 @@
################################################################ ################################################################
# $Id$
################################################################
# #
# $Id$ # 98_readingsWatcher
# #
# (c) 2015,2016 Copyright: HCS,Wzut # (c) 2015,2016 Copyright: HCS,Wzut
# All rights reserved # All rights reserved
@ -13,20 +15,34 @@
# (at your option) any later version. # (at your option) any later version.
# The GNU General Public License can be found at # The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html. # http://www.gnu.org/copyleft/gpl.html.
# A copy is found in the textfile GPL.txt and important notices to the license
# from the author is found in LICENSE.txt distributed with these scripts.
# This script is distributed in the hope that it will be useful, # This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
#
#
# 2.1.0 => 06.04.20
# 2.0.0 => 05.04.20 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
# 1.3.0 => 26.01.18 use ReadingsAge
# 1.2.0 => 15.02.16 add Set, Get
# 1.1.0 => 14.02.16
# 1.0.0 => (c) HCS, first version
#
################################################################ ################################################################
package FHEM::readingsWatcher; ## no critic 'package' package FHEM::readingsWatcher; ## no critic 'package'
# das no critic könnte weg wenn die Module nicht mehr zwingend mit NN_ beginnnen müssen
use strict; use strict;
use warnings; use warnings;
use utf8; use utf8;
use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt 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); use Time::HiRes qw(gettimeofday);
use List::Util qw(uniq);
BEGIN BEGIN
{ {
@ -63,44 +79,23 @@ BEGIN
CommandSetReading CommandSetReading
CommandDeleteReading CommandDeleteReading
gettimeofday gettimeofday
TimeNow TimeNow)
)
); );
# Export to main context # Export to main
GP_Export( GP_Export( qw(Initialize) );
qw(
Initialize
)
);
} }
# Versions History intern
my %vNotesIntern =
(
"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",
"1.3.0" => "26.01.18 use ReadingsAge",
"1.2.0" => "15.02.16 add Set, Get",
"1.1.0" => "14.02.16",
"1.0.0" => "(c) HCS, first version"
);
my $hasmeta = 0; my $hasmeta = 0;
# ältere Installationen haben noch kein Meta.pm
if (-e $attr{global}{modpath}.'/FHEM/Meta.pm') if (-e $attr{global}{modpath}.'/FHEM/Meta.pm') {
{
$hasmeta = 1; $hasmeta = 1;
require FHEM::Meta; require FHEM::Meta;
} }
sub Initialize sub Initialize {
{
my $hash = shift; my $hash = shift;
$hash->{GetFn} = "FHEM::readingsWatcher::Get"; $hash->{GetFn} = "FHEM::readingsWatcher::Get";
$hash->{SetFn} = "FHEM::readingsWatcher::Set"; $hash->{SetFn} = "FHEM::readingsWatcher::Set";
@ -116,24 +111,27 @@ sub Initialize
##################################################################################### #####################################################################################
sub Define sub Define {
{
my $hash = shift; my $hash = shift;
my $def = shift; my $def = shift;
my ($name, $type, $noglobal) = split("[ \t\n]+", $def, 3); my ($name, $type, $noglobal) = split(m{ \s+ }xms, $def, 3);
if(exists($modules{readingsWatcher}{defptr})) if (exists($modules{readingsWatcher}{defptr})) {
{
my $error = 'one readingsWatcher device is already defined !'; my $error = 'one readingsWatcher device is already defined !';
Log3 $name, 1, $error; Log3 $hash, 1, $error;
return $error; return $error;
} }
$modules{readingsWatcher}{defptr} = $hash; $modules{readingsWatcher}{defptr} = $hash;
if (defined($noglobal) && ($noglobal eq 'noglobal')) if (defined($noglobal) && ($noglobal eq 'noglobal')) {
{ $hash->{DEF} = 'noglobal'; } $hash->{DEF} = 'noglobal';
else { addToAttrList('readingsWatcher'); $hash->{DEF} = 'global';} # global -> userattr }
else {
addToAttrList('readingsWatcher');
$hash->{DEF} = 'global'; # global -> userattr
}
CommandAttr(undef, "$name interval 60") unless (exists($attr{$name}{interval})); CommandAttr(undef, "$name interval 60") unless (exists($attr{$name}{interval}));
CommandAttr(undef, "$name readingActivity none") unless (exists($attr{$name}{readingActivity})); CommandAttr(undef, "$name readingActivity none") unless (exists($attr{$name}{readingActivity}));
@ -141,42 +139,49 @@ sub Define
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+5, 'FHEM::readingsWatcher::OnTimer', $hash, 0); InternalTimer(gettimeofday()+5, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
if ($hasmeta) { return $@ unless ( FHEM::Meta::SetInternals($hash) ) } if ($hasmeta) {
return $@ unless ( FHEM::Meta::SetInternals($hash) )
}
return; return;
} }
##################################################################################### #####################################################################################
sub Undefine sub Undefine {
{
my $hash = shift; my $hash = shift;
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
delete($modules{readingsWatcher}{defptr}); delete($modules{readingsWatcher}{defptr});
if ($hash->{DEF} eq 'global')
{ if ($hash->{DEF} eq 'global') { # werden die meisten haben
delFromAttrList('readingsWatcher'); # global -> userattr delFromAttrList('readingsWatcher'); # global -> userattr
my @devs = devspec2array("readingsWatcher!="); # wer hat alles ein Attribut readingsWatcher ? # wer hat alles ein Attribut readingsWatcher gesetzt ?
foreach (@devs) foreach (devspec2array("readingsWatcher!=")) {
{ delFromDevAttrList($_, 'readingsWatcher'); } # aufräumen delFromDevAttrList($_, 'readingsWatcher'); # aufräumen
} }
}
return; return;
} }
##################################################################################### #####################################################################################
sub Set sub Set {
{
my ($hash, $name, $cmd) = @_;
if ($cmd eq 'inactive') my $hash = shift;
{ my $name = shift;
my $cmd = shift // return "set $name needs at least one argument !";
if ($cmd eq 'inactive') {
readingsSingleUpdate($hash, 'state', 'inactive', 1); readingsSingleUpdate($hash, 'state', 'inactive', 1);
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
$hash->{INTERVAL} = 0; $hash->{INTERVAL} = 0;
return; return;
} }
elsif ($cmd eq 'active')
{ if ($cmd eq 'active') {
readingsSingleUpdate($hash, 'state', 'active', 1); readingsSingleUpdate($hash, 'state', 'active', 1);
$hash->{INTERVAL} = AttrVal($name,'interval',60); $hash->{INTERVAL} = AttrVal($name,'interval',60);
return; return;
@ -184,22 +189,20 @@ sub Set
return if (IsDisabled($name)); return if (IsDisabled($name));
if (($cmd eq 'checkNow') || ($cmd eq 'active')) if (($cmd eq 'checkNow') || ($cmd eq 'active')) {
{
OnTimer($hash); OnTimer($hash);
return; return;
} }
if ($cmd eq 'clearReadings') if ($cmd eq 'clearReadings') {
{
foreach (keys %{$defs{$name}{READINGS}}) # alle eignen Readings foreach (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
{ if ($_ =~ /_/) { # device_reading
if ($_ =~ /_/) # device.reading
{
readingsDelete($hash, $_); readingsDelete($hash, $_);
Log3 $name,4,"$name, delete reading $_"; Log3 $hash,4,"$name, delete reading ".$_;
} }
} }
return; return;
} }
@ -208,127 +211,186 @@ sub Set
##################################################################################### #####################################################################################
sub Get sub Get {
{
my ($hash, $name , $cmd)= @_;
return if(!$cmd);
my (@parts, $rSA, $age , @devs, $state); my $hash = shift;
my ($dw,$rw,$tw,$sw,$aw) = (6,7,7,5,3); my $name = shift;
my $cmd = shift // return "get $name needs at least one argument !";
if ($cmd eq 'devices') return getStateList($name) if ($cmd eq 'devices');
{
foreach my $deviceName (devspec2array("readingsWatcher!="))
{
$rSA = ($deviceName eq $name) ? '' : AttrVal($deviceName, 'readingsWatcher', '');
$dw = length($deviceName) if (length($deviceName) > $dw);
if ($rSA)
{
if (IsDisabled($deviceName))
{
$sw = 8 if ($sw<8);
push @devs, "$deviceName,-,-,disabled,-";
}
elsif (IsIgnored($deviceName))
{
$sw = 7 if ($sw<7);
push @devs, "$deviceName,-,-,ignored,-";
}
else
{
my @r = split(';',$rSA);
foreach (@r)
{
$_ =~ s/\+/,/g;
@parts = split(',', $_);
if (@parts > 2)
{
my $timeout = int($parts[0]);
$tw = length($timeout) if(length($timeout) > $tw);
shift @parts; # Timeoutwert
shift @parts; # Ersatzwert
foreach (@parts) # alle zu überwachenden Readings
{
$_ =~ s/^\s+|\s+$//g;
$_ = 'state' if ($_ eq 'STATE');
$rw = length($_) if(length($_) > $rw);
if (($_ eq 'state') && (ReadingsVal($deviceName,'state','') eq 'inactive'))
{
$state = 'inactive';
$age = '-';
}
else
{
$age = ReadingsAge($deviceName, $_, undef);
if (!defined($age))
{
$state = 'unknown';
$age = 'undef';
}
else
{
$state = ($age>$timeout) ? 'timeout' : 'ok';
}
}
$aw = length($age) if(length($age) > $aw);
$sw = length($state) if(length($state) > $sw);
push @devs, "$deviceName, $_, $timeout, $state, $age";
}
} # @parts >2
else
{
$sw = 16 if ($sw<16);
push @devs, "$deviceName,-,-,wrong parameters,-";
}
} # not disabled
} # rSA
}
} # foreach
if (int(@devs))
{
$dw += 2;
$rw += 2;
$sw += 2;
$aw += 2;
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,'+'; }
$s .= "\n".$line."\n";
foreach(@devs)
{
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 "unknown command $cmd, choose one of devices:noArg"; return "unknown command $cmd, choose one of devices:noArg";
} }
##################################################################################### #####################################################################################
sub OnTimer sub getStateList {
my $name = shift;
my @devs;
foreach my $device (devspec2array("readingsWatcher!=")) {
my $rSA = ($device ne $name) ? AttrVal($device, 'readingsWatcher', '') : '';
next if (!$rSA);
if (IsDisabled($device)) {
push @devs, "$device,-,-,disabled,-";
}
elsif (IsIgnored($device)) {
push @devs, "$device,-,-,ignored,-";
}
else { # valid device
push @devs , IsValidDevice($device,$rSA);
}
}
return formatStateList(@devs);
}
#####################################################################################
sub IsValidDevice {
my $device = shift;
my @devs;
foreach (split(';', shift)) { # Anzahl Regelsätze pro Device, meist nur einer
$_ =~ s/\+/,/g; # OR Readings wie normale Readingsliste behandeln
$_ =~ s/ //g;
my ($timeout,undef,@readings) = split(',', $_); # der ggf. vorhandene Ersatzstring wird hier nicht benötigt
if (!@readings) {
push @devs, "$device,-,-,wrong parameters,-";
}
else {
foreach my $reading (@readings) { # alle zu überwachenden Readings
my ($age,$state);
$reading =~ s/ //g;
if (($reading eq 'state') && (ReadingsVal($device, 'state', '') eq 'inactive')) {
$state = 'inactive';
$age = '-';
}
else {
$age = ReadingsAge($device, $reading, undef);
if (!defined($age)) {
$state = 'unknown';
$age = 'undef';
}
else {
$state = (int($age) > int($timeout)) ? 'timeout' : 'ok';
}
}
push @devs, "$device,$reading,$timeout,$state,$age";
}
}
}
return @devs;
}
#####################################################################################
sub formatStateList {
# Device | Reading | Timeout | State | Age
# -------+------------+---------+---------+--------
# CUL | credit10ms | 300 | ok | 56
# lamp | state | 900 | timeout | 3799924
# -------+------------+---------+---------+--------
my (@devs) = @_;
return 'Sorry, no devices with valid attribute readingsWatcher found !' if (!@devs);
my ($dw,$rw,$tw,$sw,$aw) = (6,7,7,5,3); # Startbreiten, bzw. Mindestbreite durch Überschrift
foreach (@devs) {
my ($d,$r,$t,$s,$g) = split(',', $_);
# die tatsächlichen Breiten aus den vorhandenen Werten ermitteln
$dw = length($d) if (length($d) > $dw);
$rw = length($r) if (length($r) > $rw);
$tw = length($t) if (length($t) > $tw);
$sw = length($s) if (length($s) > $sw);
$aw = length($g) if (length($g) > $aw);
}
my $head = 'Device ' .(' ' x ($dw-6))
.'| Reading '.(' ' x ($rw-7)).'| '
.(' ' x ($tw-7)).'Timeout | '
.(' ' x ($sw-5)).'State | '
.(' ' x ($aw-3)).'Age';
my $separator = ('-' x length($head));
while ( $head =~ m/\|/g ) { # alle | Positionen durch + ersetzen
substr $separator, (pos($head)-1), 1, '+';
}
$head .= "\n".$separator."\n";
my $s;
foreach (@devs) {
my ($d,$r,$t,$e,$g) = split(',', $_);
$s .= $d . (' ' x ($dw - length($d))).' '; # left-align Device
$s .= '| '. $r . (' ' x ($rw - length($r))).' '; # left-align Reading
$s .= '| ' . (' ' x ($tw - length($t))).$t.' '; # Timeout right-align
$s .= '| ' . (' ' x ($sw - length($e))).$e.' '; # State right-align
$s .= '| ' . (' ' x ($aw - length($g))).$g; # Age right-align
$s .= "\n";
}
return $head.$s.$separator;
}
#####################################################################################
sub Attr {
my ($cmd, $name, $attrName, $attrVal) = @_;
return 'attribute not allowed for self !' if ($attrName eq 'readingsWatcher');
my $hash = $defs{$name};
if ($cmd eq 'set')
{ {
if ($attrName eq 'disable') {
readingsSingleUpdate($hash, 'state', 'disabled', 1) if (int($attrVal) == 1);
OnTimer($hash) if (int($attrVal) == 0);
return;
}
if (($attrName eq 'readingActivity') && ($attrVal eq 'state')) {
my $error = 'forbidden value state !';
Log3 $hash,1,"$name, readingActivity $error";
return $error;
}
}
if (($cmd eq 'del') && ($attrName eq 'disable')) {
OnTimer($hash);
}
return;
}
#####################################################################################
sub OnTimer {
my $hash = shift; my $hash = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $interval = AttrNum($name, 'interval', 0); my $interval = AttrNum($name, 'interval', 0);
$hash->{INTERVAL} = $interval; $hash->{INTERVAL} = $interval;
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
@ -339,224 +401,176 @@ sub OnTimer
readingsSingleUpdate($hash, 'state', 'disabled', 0) if (IsDisabled($name)); readingsSingleUpdate($hash, 'state', 'disabled', 0) if (IsDisabled($name));
return if ( IsDisabled($name) || !$init_done ); return if ( IsDisabled($name) || !$init_done );
my ($timeOutState, $errorValue, $timeout, $error, $readingsList); my ($readingsList , @devices);
my ($rSA, $age, @devices, $rts, @parts, @devs); my ($alive_count , $readings_count) = (0, 0);
my ($alives, $deads, $state, $readings, $skipped, $timeouts) = (0, 0, '', 0, 0, 0); my @toDevs = ();
my @timeOutdevs = (); my @deadDevs = (); my @skipDevs = (); my @deadDevs = ();
my @skipDevs = ();
my ($readingActifity,$dead,$alive) = split(':', AttrVal($name, 'readingActivity', 'none:dead:alive'));
foreach (keys %{$defs{$name}{READINGS}}) # alle eignen Readings $dead //= 'dead'; # if (!defined($dead));
{ $readingsList .= $_ .',' if ($_ =~ /_/); }# nur die mit _ im Namen $alive //= 'alive'; # if (!defined($alive));
$readingActifity = '' if ($readingActifity eq 'none');
@devs = devspec2array("readingsWatcher!="); foreach (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
$readingsList .= $_ .',' if ( $_ =~ /_/ ); # nur die Readings mit _ im Namen (Device_Reading)
my ($areading,$dead,$alive) = split(":",AttrVal($name,'readingActivity','none:dead:alive')); }
$dead = 'dead' if (!defined($dead));
$alive= 'alive' if (!defined($alive));
$areading = '' if ($areading eq 'none');
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
foreach my $deviceName (@devs) foreach my $device (devspec2array('readingsWatcher!=')) {
{
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', ''); my $or_and = 0; # Readings als OR auswerten
my ($d_a, $d_d) = (0,0); # Device_alives , Device_deads
my $timeOutState = '';
if($rSA && !IsDisabled($deviceName) && !IsIgnored($deviceName)) my $rSA = ($device eq $name) ? '' : AttrVal($device, 'readingsWatcher', '');
{
push @devices, $deviceName if !grep {/$deviceName/} @devices; # keine doppelten Namen
$or_and = 1 if (index($rSA,'+') != -1); # Readings beim auswerten AND next if (!$rSA || IsDisabled($device) || IsIgnored($device));
push @devices, $device; # if !grep {/$device/} @devices; keine doppelten Namen
$or_and = 1 if (index($rSA,'+') != -1); # Readings als AND auswerten
$rSA =~ s/\+/,/g ; # eventuell vorhandene + auch in Komma wandeln $rSA =~ s/\+/,/g ; # eventuell vorhandene + auch in Komma wandeln
# rSA: timeout, errorValue, reading1, reading2, reading3, ... # rSA: timeout, errorValue, reading1, reading2, reading3, ...
# 120,---,temperature,humidity,battery # 120,---,temperature,humidity,battery
# or 900,,current,eState / no errorValue = do not change reading # or 900,,current,eState / no errorValue = do not change reading
my @r = split(';', $rSA); my $ok_device = 0;
foreach (@r)
{
@parts = split(',', $_);
if (@parts > 2) foreach (split(';', $rSA)) {
{
my ($timeout, $errorValue, @readings_ar) = split(',', $_);
if (@readings_ar) {
$ok_device = 1; $ok_device = 1;
$timeout = int($parts[0]); $timeout = int($timeout);
$errorValue = $parts[1]; # = leer, Readings des Device nicht anfassen !
# die ersten beiden brauchen wir nicht mehr
shift @parts; shift @parts;
foreach (@parts) # alle zu überwachenden Readings
{
$_ =~ s/^\s+|\s+$//g; # $_ = Reading Name
$state = 0;
if ($_ eq 'STATE')
{
$_ = 'state'; $state = 1; # Sonderfall STATE
} }
$age = ReadingsAge($deviceName, $_, undef); foreach my $reading (@readings_ar) { # alle zu überwachenden Readings
if (defined($age)) $reading =~ s/ //g;
{ my $state = 0;
$readings++;
if (($age > $timeout) && ($timeout>0)) if ($reading eq 'STATE') { # Sonderfall STATE
{
push @timeOutdevs, $deviceName if !grep {/$deviceName/} @timeOutdevs; $reading = 'state';
$timeOutState = "timeout"; $state = 1;
}
my $age = ReadingsAge($device, $reading, undef);
if (defined($age)) {
$readings_count++;
if (($age > $timeout) && ($timeout > 0)) {
push @toDevs, $device; # if (!grep {/$device/} @toDevs);
$timeOutState = 'timeout';
$d_d++; # Device Tote $d_d++; # Device Tote
$timeouts++; my $rts = ReadingsTimestamp($device, $reading, 0);
$rts = ReadingsTimestamp($deviceName, $_,0); setReadingsVal($defs{$device}, $reading, $errorValue, $rts) if ($rts && ($errorValue ne '')); # leise setzen ohne Event
setReadingsVal($defs{$deviceName},$_,$errorValue,$rts) if (($errorValue ne'')&& $rts); # leise setzen ohne Event $defs{$device}->{STATE} = $errorValue if ($state && ($errorValue ne ''));
$defs{$deviceName}->{STATE} = $errorValue if (($errorValue ne'') && $state);
} }
else else {
{
$d_a++; # Device Lebende $d_a++; # Device Lebende
$timeOutState = "ok"; $timeOutState = 'ok';
} }
my $r = $deviceName.'_'.$_; my $d_r = $device.'_'.$reading;
readingsBulkUpdate($hash, $r, $timeOutState) if ($timeout>0); readingsBulkUpdate($hash, $d_r, $timeOutState) if ($timeout > 0);
$readingsList =~ s/$r,// if ($readingsList) ; # das Reading aus der Liste streichen, leer solange noch kein Device das Attr hat ! $readingsList =~ s/$d_r,// if ($readingsList); # das Reading aus der Liste streichen, leer solange noch kein Device das Attr hat !
if ($timeout < 1) Log3 $hash,2,"name, invalid timeout value $timeout for reading $device $reading" if ($timeout < 1);
{
$error = "Invalid timeout value $timeout for reading $deviceName $_";
Log3 $name,2,$name.', '.$error;
} }
}#age else {
else setReadingsVal($defs{$device},$reading,'unknown',TimeNow()) if ($errorValue); # leise setzen ohne Event
{ $defs{$device}->{STATE} = 'unknown' if ($errorValue && $state);
setReadingsVal($defs{$deviceName},$_,'unknown',TimeNow()) if ($errorValue); # leise setzen ohne Event Log3 $hash,3,"$name, reading Timestamp for $reading not found on device $device";
$defs{$deviceName}->{STATE} = 'unknown' if ($errorValue && $state); readingsBulkUpdate($hash, $device.'_'.$reading, 'no Timestamp');
Log3 $name,3,$name.', reading Timestamp for '.$_.' not found on device '.$deviceName;
readingsBulkUpdate($hash, $deviceName.'_'.$_, 'no Timestamp');
} }
}# foreach @parts , Reading }
}# parts > 2 } # Anzahl Readings Sätze im Device
} # Anzahl Readings Sätze im Device, meist nur einer
if ($ok_device && $timeOutState) if ($ok_device && $timeOutState) {
{ my $error;
if ((!$or_and && $d_d) || ($or_and && !$d_a)) # tot bei OR und mindestens einem Toten || AND aber kein noch Lebender
{
$error = CommandSetReading(undef, "$deviceName $areading $dead") if ($areading);
$deads++; # dead devices
push @deadDevs, $deviceName;
}
else # wenn es nicht tot ist muss es eigentlich noch leben ....
{
$error = CommandSetReading(undef, "$deviceName $areading $alive") if ($areading);
$alives++; # alive devices
}
Log3 $name,2,$name.', '.$error if ($error);
}
else
{
Log3 $name,2,$name.', insufficient parameters for device '.$deviceName.' - skipped !';
$skipped++;
CommandSetReading(undef, "$deviceName $areading unknown") if ($areading);
push @skipDevs, $deviceName;
}
} # defined($rSA) && !IsDisabled($deviceName) && !IsIgnored($deviceName)
} # foreach $deviceName
readingsBulkUpdate($hash,'readings' , $readings); if ((!$or_and && $d_d) || ($or_and && !$d_a)) { # tot bei OR und mindestens einem Toten || AND aber kein noch Lebender
$error = CommandSetReading(undef, "$device $readingActifity $dead") if ($readingActifity);
push @deadDevs, $device; # dead devices
}
else { # wenn es nicht tot ist müsste es eigentlich noch leben ....
$error = CommandSetReading(undef, "$device $readingActifity $alive") if ($readingActifity);
$alive_count ++; # alive devices
}
Log3 $hash,2,"$name, $error" if ($error);
}
else {
Log3 $hash,2,"$name, insufficient parameters for device $device - skipped !";
CommandSetReading(undef, "$device $readingActifity unknown") if ($readingActifity);
push @skipDevs, $device;
}
} # foreach device
# eventuell doppelte Einträge aus allen vier Listen entfernen
@devices = List::Util::uniq @devices if (@devices);
@toDevs = List::Util::uniq @toDevs if (@toDevs);
@skipDevs = List::Util::uniq @skipDevs if (@skipDevs);
@deadDevs = List::Util::uniq @deadDevs if (@deadDevs);
readingsBulkUpdate($hash, 'readings' , $readings_count);
readingsBulkUpdate($hash, 'devices' , int(@devices)); readingsBulkUpdate($hash, 'devices' , int(@devices));
readingsBulkUpdate($hash,'alive' , $alives); readingsBulkUpdate($hash, 'alive' , $alive_count);
readingsBulkUpdate($hash,'dead' , $deads); readingsBulkUpdate($hash, 'dead' , int(@deadDevs));
readingsBulkUpdate($hash,'skipped' , $skipped) if ($skipped); readingsBulkUpdate($hash, 'skipped' , int(@skipDevs));
readingsBulkUpdate($hash,'timeouts' , $timeouts); readingsBulkUpdate($hash, 'timeouts' , int(@toDevs));
readingsBulkUpdate($hash,'state' , ($timeouts) ? 'timeout' : 'ok'); readingsBulkUpdate($hash, 'state' , (@toDevs) ? 'timeout' : 'ok');
readingsDelete($hash, 'skipped') if (!$skipped && AttrNum($name,'deleteUnusedReadings','1')); # jetzt nicht aktualisierte Readings markieren oder gleich ganz löschen
# Vorwahl via Attribut deleteUnusedReadings
clearReadings($name,$readingsList) if ($readingsList);
# nicht aktualisierte Readings markieren oder löschen (@devices) ? readingsBulkUpdate($hash, '.associatedWith' , join(',', @devices)) : readingsDelete($hash, '.associatedWith');
if ($readingsList) (@toDevs) ? readingsBulkUpdate($hash, 'timeoutDevs', join(',', @toDevs)) : readingsBulkUpdate($hash, 'toDevs', 'none');
{ (@deadDevs) ? readingsBulkUpdate($hash, 'deadDevs', join(',', @deadDevs)) : readingsBulkUpdate($hash, 'deadDevs', 'none');
my @ar = split(",",$readingsList); (@skipDevs) ? readingsBulkUpdate($hash, 'skippedDevs', join(',', @skipDevs)) : readingsBulkUpdate($hash, 'skippedDevs','none');
foreach (@ar)
{
if ($_)
{
if (AttrNum($name,'deleteUnusedReadings',1))
{
readingsDelete($hash, $_);
Log3 $name,3,$name.', delete unused reading '.$_;
}
else
{
readingsBulkUpdate($hash, $_ , 'unused');
Log3 $name,4,$name.', unused reading '.$_;
}
}
}
}
if (@devices)
{ readingsBulkUpdate($hash,'.associatedWith' , join(',',@devices)); }
else
{ readingsDelete($hash, '.associatedWith'); }
if (int(@timeOutdevs))
{ readingsBulkUpdate($hash,'timeoutdevs',join(',',@timeOutdevs));}
else
{ readingsBulkUpdate($hash,'timeoutdevs','none');}
if (int(@deadDevs))
{ readingsBulkUpdate($hash,'deadDevs',join(',',@deadDevs));}
else
{ readingsBulkUpdate($hash,'deadDevs','none');}
if (int(@skipDevs))
{ readingsBulkUpdate($hash,'skippedDevs',join(',',@skipDevs));}
else
{ readingsBulkUpdate($hash,'skippedDevs','none');}
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
return; return;
} }
sub Attr #####################################################################################
{
my ($cmd, $name, $attrName, $attrVal) = @_; sub clearReadings {
my $name = shift;
my $hash = $defs{$name}; my $hash = $defs{$name};
return 'not allowed !' if ($name eq $attrName); foreach my $reading (split(',', shift)) # Liste der aktiven Readings
{
next if (!$reading);
if ($cmd eq 'set') if (AttrNum($name, 'deleteUnusedReadings', 1))
{ {
if ($attrName eq 'disable') readingsDelete($hash, $reading);
Log3 $hash, 3, "$name, delete unused reading $reading";
}
else
{ {
readingsSingleUpdate($hash,'state','disabled',1) if (int($attrVal) == 1); readingsBulkUpdate($hash, $reading, 'unused');
OnTimer($hash) if (int($attrVal) == 0); Log3 $hash, 4, "$name, unused reading $reading";
return; }
} }
if (($attrName eq 'readingActivity') && ($attrVal eq 'state'))
{
my $error = 'forbidden value state !';
Log3 $name,1,"$name, readingActivity $error";
return $error;
}
}
elsif ($cmd eq 'del')
{
if ($attrName eq 'disable')
{
OnTimer($hash);
}
}
return; return;
} }
##################################################################################### #####################################################################################
1; 1;
=pod =pod
@ -724,7 +738,7 @@ sub Attr
"supervision", "supervision",
"überwachung" "überwachung"
], ],
"version": "2.0.0", "version": "2.1.0",
"release_status": "stable", "release_status": "stable",
"author": [ "author": [
"Wzut" "Wzut"
@ -733,15 +747,14 @@ sub Attr
"Wzut" "Wzut"
], ],
"x_fhem_maintainer_github": [ "x_fhem_maintainer_github": [
"Wzut"
], ],
"prereqs": { "prereqs": {
"runtime": { "runtime": {
"requires": { "requires": {
"FHEM": 5.00918799, "FHEM": 5.00918799,
"perl": 5.014,
"GPUtils": 0, "GPUtils": 0,
"Time::HiRes": 0 "Time::HiRes": 0,
"List::Util": 0
}, },
"recommends": { "recommends": {
"FHEM::Meta": 0 "FHEM::Meta": 0