mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
98_readingsWatcher: refactoring
git-svn-id: https://svn.fhem.de/fhem/trunk@21629 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
50d81b8beb
commit
ec7f5baf7f
@ -1,6 +1,8 @@
|
||||
################################################################
|
||||
# $Id$
|
||||
################################################################
|
||||
#
|
||||
# $Id$
|
||||
# 98_readingsWatcher
|
||||
#
|
||||
# (c) 2015,2016 Copyright: HCS,Wzut
|
||||
# All rights reserved
|
||||
@ -13,20 +15,34 @@
|
||||
# (at your option) any later version.
|
||||
# The GNU General Public License can be found at
|
||||
# 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,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# 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'
|
||||
# das no critic könnte weg wenn die Module nicht mehr zwingend mit NN_ beginnnen müssen
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
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);
|
||||
use List::Util qw(uniq);
|
||||
|
||||
BEGIN
|
||||
{
|
||||
@ -63,44 +79,23 @@ BEGIN
|
||||
CommandSetReading
|
||||
CommandDeleteReading
|
||||
gettimeofday
|
||||
TimeNow
|
||||
)
|
||||
TimeNow)
|
||||
);
|
||||
|
||||
# Export to main context
|
||||
GP_Export(
|
||||
qw(
|
||||
Initialize
|
||||
)
|
||||
);
|
||||
# Export to main
|
||||
GP_Export( 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;
|
||||
|
||||
if (-e $attr{global}{modpath}.'/FHEM/Meta.pm')
|
||||
{
|
||||
# ältere Installationen haben noch kein Meta.pm
|
||||
if (-e $attr{global}{modpath}.'/FHEM/Meta.pm') {
|
||||
$hasmeta = 1;
|
||||
require FHEM::Meta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub Initialize
|
||||
{
|
||||
sub Initialize {
|
||||
|
||||
my $hash = shift;
|
||||
$hash->{GetFn} = "FHEM::readingsWatcher::Get";
|
||||
$hash->{SetFn} = "FHEM::readingsWatcher::Set";
|
||||
@ -116,90 +111,98 @@ sub Initialize
|
||||
|
||||
#####################################################################################
|
||||
|
||||
sub Define
|
||||
{
|
||||
sub Define {
|
||||
|
||||
my $hash = 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 !';
|
||||
Log3 $name, 1, $error;
|
||||
Log3 $hash, 1, $error;
|
||||
return $error;
|
||||
}
|
||||
|
||||
$modules{readingsWatcher}{defptr} = $hash;
|
||||
|
||||
if (defined($noglobal) && ($noglobal eq 'noglobal'))
|
||||
{ $hash->{DEF} = 'noglobal'; }
|
||||
else { addToAttrList('readingsWatcher'); $hash->{DEF} = 'global';} # global -> userattr
|
||||
if (defined($noglobal) && ($noglobal eq 'noglobal')) {
|
||||
$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 ($hasmeta) { return $@ unless ( FHEM::Meta::SetInternals($hash) ) }
|
||||
if ($hasmeta) {
|
||||
return $@ unless ( FHEM::Meta::SetInternals($hash) )
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################################################################
|
||||
|
||||
sub Undefine
|
||||
{
|
||||
sub Undefine {
|
||||
|
||||
my $hash = shift;
|
||||
RemoveInternalTimer($hash);
|
||||
delete($modules{readingsWatcher}{defptr});
|
||||
if ($hash->{DEF} eq 'global')
|
||||
{
|
||||
|
||||
if ($hash->{DEF} eq 'global') { # werden die meisten haben
|
||||
|
||||
delFromAttrList('readingsWatcher'); # global -> userattr
|
||||
my @devs = devspec2array("readingsWatcher!="); # wer hat alles ein Attribut readingsWatcher ?
|
||||
foreach (@devs)
|
||||
{ delFromDevAttrList($_, 'readingsWatcher'); } # aufräumen
|
||||
# wer hat alles ein Attribut readingsWatcher gesetzt ?
|
||||
foreach (devspec2array("readingsWatcher!=")) {
|
||||
delFromDevAttrList($_, 'readingsWatcher'); # aufräumen
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################################################################
|
||||
|
||||
sub Set
|
||||
{
|
||||
my ($hash, $name, $cmd) = @_;
|
||||
sub Set {
|
||||
|
||||
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);
|
||||
RemoveInternalTimer($hash);
|
||||
$hash->{INTERVAL} = 0;
|
||||
return;
|
||||
}
|
||||
elsif ($cmd eq 'active')
|
||||
{
|
||||
|
||||
if ($cmd eq 'active') {
|
||||
readingsSingleUpdate($hash, 'state', 'active', 1);
|
||||
$hash->{INTERVAL} = AttrVal($name,'interval',60);
|
||||
return;
|
||||
}
|
||||
|
||||
return if(IsDisabled($name));
|
||||
return if (IsDisabled($name));
|
||||
|
||||
if (($cmd eq 'checkNow') || ($cmd eq 'active'))
|
||||
{
|
||||
if (($cmd eq 'checkNow') || ($cmd eq 'active')) {
|
||||
OnTimer($hash);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($cmd eq 'clearReadings')
|
||||
{
|
||||
foreach (keys %{$defs{$name}{READINGS}}) # alle eignen Readings
|
||||
{
|
||||
if ($_ =~ /_/) # device.reading
|
||||
{
|
||||
if ($cmd eq 'clearReadings') {
|
||||
|
||||
foreach (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
|
||||
if ($_ =~ /_/) { # device_reading
|
||||
readingsDelete($hash, $_);
|
||||
Log3 $name,4,"$name, delete reading $_";
|
||||
Log3 $hash,4,"$name, delete reading ".$_;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -208,355 +211,366 @@ sub Set
|
||||
|
||||
#####################################################################################
|
||||
|
||||
sub Get
|
||||
{
|
||||
my ($hash, $name , $cmd)= @_;
|
||||
return if(!$cmd);
|
||||
sub Get {
|
||||
|
||||
my (@parts, $rSA, $age , @devs, $state);
|
||||
my ($dw,$rw,$tw,$sw,$aw) = (6,7,7,5,3);
|
||||
my $hash = shift;
|
||||
my $name = shift;
|
||||
my $cmd = shift // return "get $name needs at least one argument !";
|
||||
|
||||
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 getStateList($name) if ($cmd eq 'devices');
|
||||
|
||||
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 $name = $hash->{NAME};
|
||||
my $interval = AttrNum($name, 'interval', 0);
|
||||
|
||||
$hash->{INTERVAL} = $interval;
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
return if (!$interval);
|
||||
|
||||
InternalTimer(gettimeofday()+$interval, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
|
||||
InternalTimer(gettimeofday() + $interval, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
|
||||
|
||||
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 ($rSA, $age, @devices, $rts, @parts, @devs);
|
||||
my ($alives, $deads, $state, $readings, $skipped, $timeouts) = (0, 0, '', 0, 0, 0);
|
||||
my @timeOutdevs = (); my @deadDevs = (); my @skipDevs = ();
|
||||
my ($readingsList , @devices);
|
||||
my ($alive_count , $readings_count) = (0, 0);
|
||||
my @toDevs = ();
|
||||
my @deadDevs = ();
|
||||
my @skipDevs = ();
|
||||
my ($readingActifity,$dead,$alive) = split(':', AttrVal($name, 'readingActivity', 'none:dead:alive'));
|
||||
|
||||
foreach (keys %{$defs{$name}{READINGS}}) # alle eignen Readings
|
||||
{ $readingsList .= $_ .',' if ($_ =~ /_/); }# nur die mit _ im Namen
|
||||
$dead //= 'dead'; # if (!defined($dead));
|
||||
$alive //= 'alive'; # if (!defined($alive));
|
||||
$readingActifity = '' if ($readingActifity eq 'none');
|
||||
|
||||
@devs = devspec2array("readingsWatcher!=");
|
||||
|
||||
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');
|
||||
foreach (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
|
||||
$readingsList .= $_ .',' if ( $_ =~ /_/ ); # nur die Readings mit _ im Namen (Device_Reading)
|
||||
}
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
|
||||
foreach my $deviceName (@devs)
|
||||
{
|
||||
my $or_and = 0; # Readings beim auswerten OR
|
||||
my ($d_a,$d_d,$ok_device) = (0,0,0);
|
||||
$timeOutState = '';
|
||||
foreach my $device (devspec2array('readingsWatcher!=')) {
|
||||
|
||||
$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))
|
||||
{
|
||||
push @devices, $deviceName if !grep {/$deviceName/} @devices; # keine doppelten Namen
|
||||
my $rSA = ($device eq $name) ? '' : AttrVal($device, 'readingsWatcher', '');
|
||||
|
||||
$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: timeout, errorValue, reading1, reading2, reading3, ...
|
||||
# 120,---,temperature,humidity,battery
|
||||
# or 900,,current,eState / no errorValue = do not change reading
|
||||
|
||||
my @r = split(';', $rSA);
|
||||
foreach (@r)
|
||||
{
|
||||
@parts = split(',', $_);
|
||||
my $ok_device = 0;
|
||||
|
||||
if (@parts > 2)
|
||||
{
|
||||
foreach (split(';', $rSA)) {
|
||||
|
||||
my ($timeout, $errorValue, @readings_ar) = split(',', $_);
|
||||
|
||||
if (@readings_ar) {
|
||||
$ok_device = 1;
|
||||
$timeout = int($parts[0]);
|
||||
$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
|
||||
$timeout = int($timeout);
|
||||
}
|
||||
|
||||
$age = ReadingsAge($deviceName, $_, undef);
|
||||
foreach my $reading (@readings_ar) { # alle zu überwachenden Readings
|
||||
|
||||
if (defined($age))
|
||||
{
|
||||
$readings++;
|
||||
$reading =~ s/ //g;
|
||||
my $state = 0;
|
||||
|
||||
if (($age > $timeout) && ($timeout>0))
|
||||
{
|
||||
push @timeOutdevs, $deviceName if !grep {/$deviceName/} @timeOutdevs;
|
||||
$timeOutState = "timeout";
|
||||
if ($reading eq 'STATE') { # Sonderfall STATE
|
||||
|
||||
$reading = 'state';
|
||||
$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
|
||||
$timeouts++;
|
||||
$rts = ReadingsTimestamp($deviceName, $_,0);
|
||||
setReadingsVal($defs{$deviceName},$_,$errorValue,$rts) if (($errorValue ne'')&& $rts); # leise setzen ohne Event
|
||||
$defs{$deviceName}->{STATE} = $errorValue if (($errorValue ne'') && $state);
|
||||
my $rts = ReadingsTimestamp($device, $reading, 0);
|
||||
setReadingsVal($defs{$device}, $reading, $errorValue, $rts) if ($rts && ($errorValue ne '')); # leise setzen ohne Event
|
||||
$defs{$device}->{STATE} = $errorValue if ($state && ($errorValue ne ''));
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$d_a++; # Device Lebende
|
||||
$timeOutState = "ok";
|
||||
$timeOutState = 'ok';
|
||||
}
|
||||
|
||||
my $r = $deviceName.'_'.$_;
|
||||
my $d_r = $device.'_'.$reading;
|
||||
|
||||
readingsBulkUpdate($hash, $r, $timeOutState) if ($timeout>0);
|
||||
$readingsList =~ s/$r,// if ($readingsList) ; # das Reading aus der Liste streichen, leer solange noch kein Device das Attr hat !
|
||||
readingsBulkUpdate($hash, $d_r, $timeOutState) if ($timeout > 0);
|
||||
$readingsList =~ s/$d_r,// if ($readingsList); # das Reading aus der Liste streichen, leer solange noch kein Device das Attr hat !
|
||||
|
||||
if ($timeout < 1)
|
||||
{
|
||||
$error = "Invalid timeout value $timeout for reading $deviceName $_";
|
||||
Log3 $name,2,$name.', '.$error;
|
||||
Log3 $hash,2,"name, invalid timeout value $timeout for reading $device $reading" if ($timeout < 1);
|
||||
}
|
||||
}#age
|
||||
else
|
||||
{
|
||||
setReadingsVal($defs{$deviceName},$_,'unknown',TimeNow()) if ($errorValue); # leise setzen ohne Event
|
||||
$defs{$deviceName}->{STATE} = 'unknown' if ($errorValue && $state);
|
||||
Log3 $name,3,$name.', reading Timestamp for '.$_.' not found on device '.$deviceName;
|
||||
readingsBulkUpdate($hash, $deviceName.'_'.$_, 'no Timestamp');
|
||||
else {
|
||||
setReadingsVal($defs{$device},$reading,'unknown',TimeNow()) if ($errorValue); # leise setzen ohne Event
|
||||
$defs{$device}->{STATE} = 'unknown' if ($errorValue && $state);
|
||||
Log3 $hash,3,"$name, reading Timestamp for $reading not found on device $device";
|
||||
readingsBulkUpdate($hash, $device.'_'.$reading, 'no Timestamp');
|
||||
}
|
||||
}# foreach @parts , Reading
|
||||
}# parts > 2
|
||||
} # Anzahl Readings Sätze im Device, meist nur einer
|
||||
}
|
||||
} # Anzahl Readings Sätze im Device
|
||||
|
||||
if ($ok_device && $timeOutState)
|
||||
{
|
||||
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
|
||||
if ($ok_device && $timeOutState) {
|
||||
my $error;
|
||||
|
||||
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 @ar = split(",",$readingsList);
|
||||
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 ((!$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
|
||||
|
||||
if (@devices)
|
||||
{ readingsBulkUpdate($hash,'.associatedWith' , join(',',@devices)); }
|
||||
else
|
||||
{ readingsDelete($hash, '.associatedWith'); }
|
||||
# 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);
|
||||
|
||||
if (int(@timeOutdevs))
|
||||
{ readingsBulkUpdate($hash,'timeoutdevs',join(',',@timeOutdevs));}
|
||||
else
|
||||
{ readingsBulkUpdate($hash,'timeoutdevs','none');}
|
||||
readingsBulkUpdate($hash, 'readings' , $readings_count);
|
||||
readingsBulkUpdate($hash, 'devices' , int(@devices));
|
||||
readingsBulkUpdate($hash, 'alive' , $alive_count);
|
||||
readingsBulkUpdate($hash, 'dead' , int(@deadDevs));
|
||||
readingsBulkUpdate($hash, 'skipped' , int(@skipDevs));
|
||||
readingsBulkUpdate($hash, 'timeouts' , int(@toDevs));
|
||||
readingsBulkUpdate($hash, 'state' , (@toDevs) ? 'timeout' : 'ok');
|
||||
|
||||
if (int(@deadDevs))
|
||||
{ readingsBulkUpdate($hash,'deadDevs',join(',',@deadDevs));}
|
||||
else
|
||||
{ readingsBulkUpdate($hash,'deadDevs','none');}
|
||||
# jetzt nicht aktualisierte Readings markieren oder gleich ganz löschen
|
||||
# Vorwahl via Attribut deleteUnusedReadings
|
||||
clearReadings($name,$readingsList) if ($readingsList);
|
||||
|
||||
if (int(@skipDevs))
|
||||
{ readingsBulkUpdate($hash,'skippedDevs',join(',',@skipDevs));}
|
||||
else
|
||||
{ readingsBulkUpdate($hash,'skippedDevs','none');}
|
||||
(@devices) ? readingsBulkUpdate($hash, '.associatedWith' , join(',', @devices)) : readingsDelete($hash, '.associatedWith');
|
||||
(@toDevs) ? readingsBulkUpdate($hash, 'timeoutDevs', join(',', @toDevs)) : readingsBulkUpdate($hash, 'toDevs', 'none');
|
||||
(@deadDevs) ? readingsBulkUpdate($hash, 'deadDevs', join(',', @deadDevs)) : readingsBulkUpdate($hash, 'deadDevs', 'none');
|
||||
(@skipDevs) ? readingsBulkUpdate($hash, 'skippedDevs', join(',', @skipDevs)) : readingsBulkUpdate($hash, 'skippedDevs','none');
|
||||
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Attr
|
||||
{
|
||||
my ($cmd, $name, $attrName, $attrVal) = @_;
|
||||
#####################################################################################
|
||||
|
||||
sub clearReadings {
|
||||
|
||||
my $name = shift;
|
||||
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')
|
||||
{
|
||||
readingsSingleUpdate($hash,'state','disabled',1) if (int($attrVal) == 1);
|
||||
OnTimer($hash) if (int($attrVal) == 0);
|
||||
return;
|
||||
readingsDelete($hash, $reading);
|
||||
Log3 $hash, 3, "$name, delete unused reading $reading";
|
||||
}
|
||||
if (($attrName eq 'readingActivity') && ($attrVal eq 'state'))
|
||||
else
|
||||
{
|
||||
my $error = 'forbidden value state !';
|
||||
Log3 $name,1,"$name, readingActivity $error";
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
elsif ($cmd eq 'del')
|
||||
{
|
||||
if ($attrName eq 'disable')
|
||||
{
|
||||
OnTimer($hash);
|
||||
readingsBulkUpdate($hash, $reading, 'unused');
|
||||
Log3 $hash, 4, "$name, unused reading $reading";
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################################################################
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
@ -724,7 +738,7 @@ sub Attr
|
||||
"supervision",
|
||||
"überwachung"
|
||||
],
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"release_status": "stable",
|
||||
"author": [
|
||||
"Wzut"
|
||||
@ -733,15 +747,14 @@ sub Attr
|
||||
"Wzut"
|
||||
],
|
||||
"x_fhem_maintainer_github": [
|
||||
"Wzut"
|
||||
],
|
||||
"prereqs": {
|
||||
"runtime": {
|
||||
"requires": {
|
||||
"FHEM": 5.00918799,
|
||||
"perl": 5.014,
|
||||
"GPUtils": 0,
|
||||
"Time::HiRes": 0
|
||||
"Time::HiRes": 0,
|
||||
"List::Util": 0
|
||||
},
|
||||
"recommends": {
|
||||
"FHEM::Meta": 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user