fhem-mirror/FHEM/98_readingsWatcher.pm
2021-10-07 07:39:29 +00:00

819 lines
26 KiB
Perl

################################################################
# $Id$
################################################################
#
# 98_readingsWatcher
#
# (c) 2015,2016 Copyright: HCS,Wzut
# All rights reserved
#
# FHEM Forum : https://forum.fhem.de/index.php/topic,49408.0.html
#
# This code is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# The GNU General Public License can be found at
# http://www.gnu.org/copyleft/gpl.html.
# 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.3 => 20.04.20 remove all $_ , add attribut delimiter
# 2.1.2 => 16.04.20 remove undef value for ReadingsAge
# 2.1.1 => 14.04.20 remove List::Utils
# 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);
BEGIN
{
# Import from main::
GP_Import(
qw(
attr
AttrVal
AttrNum
CommandAttr
addToAttrList
delFromAttrList
delFromDevAttrList
defs
devspec2array
init_done
InternalTimer
RemoveInternalTimer
IsDisabled
IsIgnored
Log3
modules
readingsSingleUpdate
readingsBulkUpdate
readingsBeginUpdate
readingsDelete
readingsEndUpdate
readingFnAttributes
ReadingsNum
ReadingsAge
ReadingsTimestamp
ReadingsVal
setReadingsVal
CommandSetReading
CommandDeleteReading
gettimeofday
TimeNow)
);
# Export to main
GP_Export( qw(Initialize) );
}
my $hasmeta = 0;
# ältere Installationen haben noch kein Meta.pm
if (-e $attr{global}{modpath}.'/FHEM/Meta.pm') {
$hasmeta = 1;
require FHEM::Meta;
}
my @stateDevs;
my @toDevs;
my @deadDevs;
my @skipDevs;
my @allDevs;
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 delimiter:-,--,_,__ '
.$readingFnAttributes;
return FHEM::Meta::InitMod( __FILE__, $hash ) if ($hasmeta);
return;
}
#####################################################################################
sub Define {
my $hash = shift;
my $def = shift;
my ($name, $type, $noglobal) = split(m{ \s+ }xms, $def, 3);
if (exists($modules{readingsWatcher}{defptr}) && ($modules{readingsWatcher}{defptr}->{NAME} ne $name)) {
my $error = 'one readingsWatcher device is already defined !';
Log3($name, 1, $error);
return $error;
}
if (defined($noglobal) && ($noglobal eq 'noglobal')) {
$hash->{DEF} = 'noglobal';
}
else {
addToAttrList('readingsWatcher');
$hash->{DEF} = 'global'; # global -> userattr
}
$hash->{SVN} = (qw($Id$))[2];
CommandAttr(undef, "$name interval 60") unless (exists($attr{$name}{interval}));
CommandAttr(undef, "$name readingActivity none") unless (exists($attr{$name}{readingActivity}));
$modules{readingsWatcher}{defptr} = $hash;
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+5, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
Log3($name, 5, "$name, hasmeta $hasmeta");
if ($hasmeta) {
return $@ unless ( FHEM::Meta::SetInternals($hash) )
}
return;
}
#####################################################################################
sub Undefine {
my $hash = shift;
RemoveInternalTimer($hash);
delete($modules{readingsWatcher}{defptr});
if ($hash->{DEF} eq 'global') { # werden die meisten haben
delFromAttrList('readingsWatcher'); # global -> userattr
# wer hat alles ein Attribut readingsWatcher gesetzt ?
foreach my $dev (devspec2array('readingsWatcher!=')) {
delFromDevAttrList($dev, 'readingsWatcher'); # aufräumen
}
}
return;
}
#####################################################################################
sub Set {
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;
}
if ($cmd eq 'active') {
readingsSingleUpdate($hash, 'state', 'active', 1);
$hash->{INTERVAL} = AttrVal($name,'interval',60);
return;
}
return if (IsDisabled($name));
if (($cmd eq 'checkNow') || ($cmd eq 'active')) {
OnTimer($hash);
return;
}
if ($cmd eq 'clearReadings') {
my $delimiter = AttrVal($name,'delimiter','_');
foreach my $reading (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
if (index($reading, $delimiter) != -1) { # device_reading
readingsDelete($hash, $reading);
Log3($name,4,"$name, delete reading $reading");
}
}
return;
}
return "unknown argument $cmd, choose one of checkNow:noArg inactive:noArg active:noArg clearReadings:noArg";
}
#####################################################################################
sub Get {
my $hash = shift;
my $name = shift;
my $cmd = shift // return "get $name needs at least one argument !";
return getStateList($name) if ($cmd eq 'devices');
return "unknown command $cmd, choose one of devices:noArg";
}
#####################################################################################
sub getStateList {
my $name = shift;
@stateDevs = ();
foreach my $device (devspec2array('readingsWatcher!=')) {
my $rSA = ($device ne $name) ? AttrVal($device, 'readingsWatcher', '') : '';
next if ($rSA eq '');
if (IsDisabled($device)) {
push @stateDevs, "$device,-,-,disabled,-";
}
elsif (IsIgnored($device)) {
push @stateDevs, "$device,-,-,ignored,-";
}
else { # valid device
push @stateDevs , IsValidDevice($device, $rSA);
}
}
return formatStateList();
}
#####################################################################################
sub IsValidDevice {
my $device = shift;
my @ar;
foreach my $rs (split(';', shift)) { # Anzahl Regelsätze pro Device, meist nur einer
$rs =~ s/\+/,/xg; # OR Readings wie normale Readingsliste behandeln
$rs =~ s/ //g;
my ($timeout,undef,@readings) = split(',', $rs); # der ggf. vorhandene Ersatzstring wird hier nicht benötigt
return "$device,-,-,wrong parameters,-" if (!@readings);
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 ($age eq 'undef') {
$state = 'unknown';
}
else {
$state = (int($age) > int($timeout)) ? 'timeout' : 'ok';
}
}
push @ar, "$device,$reading,$timeout,$state,$age";
}
}
return "$device,?,?,?,?" if (!@ar);
return @ar;
}
#####################################################################################
sub formatStateList {
# Device | Reading | Timeout | State | Age
# -------+------------+---------+---------+--------
# CUL | credit10ms | 300 | ok | 56
# lamp | state | 900 | timeout | 3799924
# -------+------------+---------+---------+--------
return 'Sorry, no devices with valid attribute readingsWatcher found !' if (!@stateDevs);
my ($dw,$rw,$tw,$sw,$aw) = (6,7,7,5,3); # Startbreiten, bzw. Mindestbreite durch Überschrift
foreach my $dev (@stateDevs) {
my ($d,$r,$t,$s,$g) = split(',', $dev);
# die tatsächlichen Breiten aus den vorhandenen Werten ermitteln
$dw = (length($d) > $dw) ? length($d) : $dw;
$rw = (length($r) > $rw) ? length($r) : $rw;
$tw = (length($t) > $tw) ? length($t) : $tw;
$sw = (length($s) > $sw) ? length($s) : $sw;
$aw = (length($g) > $aw) ? 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{\|}xg ) { # alle | Positionen durch + ersetzen
substr $separator, (pos($head)-1), 1, '+';
}
$head .= "\n".$separator."\n";
my $s;
foreach my $dev (@stateDevs) {
my ($d,$r,$t,$e,$g) = split(',', $dev);
$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') {
RemoveInternalTimer($hash);
readingsSingleUpdate($hash, 'state', 'disabled', 1) if (int($attrVal) == 1);
InternalTimer(gettimeofday() + 2, 'FHEM::readingsWatcher::OnTimer', $hash, 0) if (int($attrVal) == 0);
return;
}
if (($attrName eq 'readingActivity') && (lc($attrVal) eq 'state')) {
my $error = 'forbidden value state !';
Log3($name, 1, "$name, readingActivity $error");
return $error;
}
}
if (($cmd eq 'del') && ($attrName eq 'disable')) {
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 2, 'FHEM::readingsWatcher::OnTimer', $hash, 0);
}
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);
readingsSingleUpdate($hash, 'state', 'disabled', 0) if (IsDisabled($name));
return if ( IsDisabled($name) || !$init_done );
@toDevs = ();
@deadDevs = ();
@skipDevs = ();
@allDevs = ();
($hash->{helper}{readingActivity},$hash->{helper}{dead},$hash->{helper}{alive}) = split(':', AttrVal($name, 'readingActivity', 'none:dead:alive'));
$hash->{helper}{dead} //= 'dead'; # if (!defined($dead));
$hash->{helper}{alive} //= 'alive'; # if (!defined($alive));
$hash->{helper}{readingActivity} = '' if ($hash->{helper}{readingActivity} eq 'none');
$hash->{helper}{delimiter} = AttrVal($name,'delimiter','_'); # -, --, _, __
foreach my $reading (keys %{$defs{$name}{READINGS}}) { # alle eigenen Readings
$hash->{helper}{readingsList} .= $reading .',' if (index($reading, $hash->{helper}{delimiter}) != -1); # nur die Readings mit _ im Namen (Device_Reading)
}
readingsBeginUpdate($hash);
foreach my $device (devspec2array('readingsWatcher!=')) {
$hash->{helper}{device} = $device;
checkDevice($hash, $device);
} # foreach device
readingsBulkUpdate($hash, 'readings' , $hash->{helper}{readings_count});
readingsBulkUpdate($hash, 'devices' , int(@allDevs));
readingsBulkUpdate($hash, 'alive' , $hash->{helper}{alive_count});
readingsBulkUpdate($hash, 'dead' , int(@deadDevs));
readingsBulkUpdate($hash, 'skipped' , int(@skipDevs));
readingsBulkUpdate($hash, 'timeouts' , int(@toDevs));
readingsBulkUpdate($hash, 'state' , (@toDevs) ? 'timeout' : 'ok');
# jetzt nicht aktualisierte Readings markieren oder gleich ganz löschen
# Vorwahl via Attribut deleteUnusedReadings
clearReadings($name) if ($hash->{helper}{readingsList});
(@allDevs) ? readingsBulkUpdate($hash, '.associatedWith', join(',', @allDevs)) : readingsDelete($hash, '.associatedWith');
(@toDevs) ? readingsBulkUpdate($hash, 'timeoutDevs', join(',', @toDevs)) : readingsBulkUpdate($hash, 'timeoutDevs', 'none');
(@deadDevs) ? readingsBulkUpdate($hash, 'deadDevs', join(',', @deadDevs)) : readingsBulkUpdate($hash, 'deadDevs', 'none');
(@skipDevs) ? readingsBulkUpdate($hash, 'skippedDevs', join(',', @skipDevs)) : readingsBulkUpdate($hash, 'skippedDevs', 'none');
readingsEndUpdate($hash, 1);
delete $hash->{helper};
return;
}
#####################################################################################
sub clearReadings {
my $name = shift;
my $hash = $defs{$name};
foreach my $reading (split(',', $hash->{helper}{readingsList})) # Liste der aktiven Readings
{
next if (!$reading);
if (AttrNum($name, 'deleteUnusedReadings', 1)) {
readingsDelete($hash, $reading);
Log3($name, 3, "$name, delete unused reading $reading");
}
else {
readingsBulkUpdate($hash, $reading, 'unused');
Log3($name, 4, "$name, unused reading $reading");
}
}
return;
}
#####################################################################################
sub checkReadings {
my $hash = shift;
my $name = $hash->{NAME};
my $device = $hash->{helper}{device};
my $timeout = $hash->{helper}{timeout};
my $errorValue = $hash->{helper}{errorValue};
my @ar = split('\|' ,$hash->{helper}{readings_ar});
foreach my $reading (@ar) { # alle zu überwachenden Readings in einem Regelsatz
$reading =~ s/ //g;
my $state = 0;
if ($reading eq 'STATE') { # Sonderfall STATE
$reading = 'state';
$state = 1;
}
my $age = ReadingsAge($device, $reading, '');
my $d_r = $device.$hash->{helper}{delimiter}.$reading;
if ($age ne '') {
$hash->{helper}{readings_count} ++;
if ($age > $timeout) {
$hash->{helper}{timeOutState} = 'timeout';
$hash->{helper}{d_d} ++; # Device Tote
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 {
$hash->{helper}{d_a} ++; # Device Lebende
$hash->{helper}{timeOutState} = 'ok';
}
readingsBulkUpdate($hash, $d_r, $hash->{helper}{timeOutState}) if ($hash->{helper}{timeOutState});
$hash->{helper}{readingsList} =~ s/$d_r,//xms if ($hash->{helper}{readingsList}); # das Reading aus der Liste streichen, leer solange noch kein Device das Attr hat !
}
else {
setReadingsVal($defs{$device},$reading,'unknown',TimeNow()) if ($errorValue); # leise setzen ohne Event
$defs{$device}->{STATE} = 'unknown' if ($errorValue && $state);
Log3($name, 3, "$name, reading Timestamp for $reading not found on device $device");
readingsBulkUpdate($hash, $d_r, 'no Timestamp');
}
} # Readings in einem Regelsatz
return;
}
sub checkDevice {
my $hash = shift;
my $name = $hash->{NAME};
my $device = shift;
my $or_and = 0; # Readings als OR auswerten
$hash->{helper}{timeOutState} = '';
$hash->{helper}{d_a} = 0;
$hash->{helper}{d_d} = 0;
my $rSA = ($device eq $name) ? '' : AttrVal($device, 'readingsWatcher', '');
return if (!$rSA || IsDisabled($device) || IsIgnored($device));
push @allDevs, $device;
$or_and = 1 if (index($rSA,'+') != -1); # Readings als AND auswerten
$rSA =~ s/\+/,/xg ; # 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 $ok_device = 0;
foreach my $sets (split(';', $rSA)) { #Anzahl Regelsätze im Device
my ($timeout, $errorValue, @readings_ar) = split(',', $sets);
$hash->{helper}{readings_ar} = join('|', @readings_ar);
$hash->{helper}{timeout} = int($timeout);
$hash->{helper}{errorValue} = $errorValue;
if (@readings_ar) {
if ($timeout > 1) {
$ok_device = 1;
}
else {
Log3($name, 2, "$name, invalid timeout value $timeout for readings $device ".join(',',@readings_ar));
delete $hash->{helper}{readings_ar}; # das werten wir danach im foreach erst gar nicht mehr aus
}
checkReadings($hash);
}
}
push @toDevs , $device if ($hash->{helper}{d_d});
if ($ok_device && $hash->{helper}{timeOutState}) {
my $error;
my $d_a = $hash->{helper}{d_a};
my $d_d = $hash->{helper}{d_d};
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 $hash->{helper}{readingActivity} $hash->{helper}{dead}") if ($hash->{helper}{readingActivity});
push @deadDevs, $device; # dead devices
}
else { # wenn es nicht tot ist müsste es eigentlich noch leben ....
$error = CommandSetReading(undef, "$device $hash->{helper}{readingActivity} $hash->{helper}{alive}") if ($hash->{helper}{readingActivity});
$hash->{helper}{alive_count} ++; # alive devices
}
Log3($name, 2, "$name, $error") if ($error);
}
else {
Log3($name, 2, "$name, insufficient parameters for device $device - skipped !");
CommandSetReading(undef, "$device $hash->{helper}{readingActivity} unknown") if ($hash->{helper}{readingActivity});
push @skipDevs, $device;
}
return;
}
1;
__END__
=pod
=over
=encoding utf8
=item helper
=item summary cyclical watching of readings updates
=item summary_DE zyklische Überwachung von Readings auf Aktualisierung
=begin html
<a name="readingsWatcher"></a>
<h3>readingsWatcher</h3>
<ul>
The module monitors readings in other modules that its readings or their times change at certain intervals and<br>
if necessary, triggers events that can be processed further with other modules (for example, notify, DOIF).<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>
<b>Define</b>
<ul>
<code>define &lt;name&gt; readingsWatcher</code><br>
<br>
Defines a readingsWatcher device.<br><br>
Afterwards each FHEM device has the new global attribute readingsWatcher<br>
This attribute must be assigned at all monitored devices as follows:<br>
Timeout in seconds, new Reading value, Reading1 of the device, Reading2, etc.<br><br>
Example: A radio thermometer sends its values at regular intervals (eg.5 seconds)<br>
If these remain off for a time X, the module can overwrite these invalid values with any other new value (Ex. ???)<br>
The readingsWatcher attribute could be set here as follows:<br><br>
<code>attr myThermo readingsWatcher 300, ???, temperature</code><br><br>
or if more than one reading should be monitored<br><br>
<code>attr myThermo readingsWatcher 300, ???, temperature, humidity</code><br><br>
If readings are only to be monitored for their update and should <b>not</b> be overwritten<br>
so the replacement string must be <b>empty</b><br><br>
Example : <code>attr myThermo readingsWatcher 300,,temperature,humidity</code><br><br>
other examples :<br>
<code>attr weather readingsWatcher 300,,temperature+humidity</code> (new)<br>
<code>attr weather readingsWatcher 300,,temperature,humidity;3600,???,battery</code>
</ul>
<a name="readingsWatcherSet"></a>
<b>Set</b>
<ul>
<li>active</li>
<li>inactive</li>
<li>checkNow</li>
<li>clearReadings</li>
</ul>
<a name="readingsWatcherGet"></a>
<b>Get</b>
<ul>
devices
</ul>
<a name="readingsWatcherAttr"></a>
<b>Attribute</b>
<ul>
<br>
<ul>
<a name="delimiter"></a><li><b>delimiter</b><br>separator for reading names ( e.g. device_reading) default _</li><br>
<a name="disable"></a><li><b>disable</b><br>deactivate/activate the device</li><br>
<a name="interval"></a><li><b>interval &lt;seconds&gt;</b><br>Time interval for continuous check (default 60)</li><br>
<a name="deleteUnusedReadings"></a><li><b>deleteUnusedReadings</b><br>delete unused readings (default 1)</li><br>
<a name="readingActivity"></a><li><b>readingActivity</b> (default none)<br>
Similar to the HomeMatic ActionDetector, the module can set its own reading in the monitored device and save the monitoring status.<br>
<code>attr &lt;name&gt; readingActivity actifity</code><br>
Creates the additional reading actifity in the monitored devices and supplies it with the status dead or alive<br>
<code>attr &lt;name&gt; readingActivity activ:0:1</code><br>
Creates the additional reading activ in the monitored devices and supplies it with the status 0 or 1
</li><br>
</ul>
</ul>
<br>
</ul>
=end html
=begin html_DE
<a name="readingsWatcher"></a>
<h3>readingsWatcher</h3>
<ul>
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>
<b>Define</b>
<ul>
<code>define &lt;name&gt; readingsWatcher</code><br>
<br>
Definiert ein readingsWatcher Device.<br><br>
Danach besitzt jedes FHEM Device das neue globale Attribut readingsWatcher<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äß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 ü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 ü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>
<code>attr wetter readingsWatcher 300,,temperature+humidity</code> (neu)<br>
<code>attr wetter readingsWatcher 300,,temperature,humidity;3600,???,battery</code>
</ul>
<a name="readingsWatcherSet"></a>
<b>Set</b>
<ul>
<li>active</li>
<li>inactive</li>
<li>checkNow</li>
<li>clearReadings</li>
</ul><br>
<a name="readingsWatcherGet"></a>
<b>Get</b>
<ul>
<li>devices</li>
</ul><br>
<a name="readingsWatcherAttr"></a>
<b>Attribute</b>
<ul>
<br>
<ul>
<a name="delimiter"></a><li><b>delimiter</b><br>Trennzeichen für Reading Namen (z.b. device_reading) default _</li><br>
<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 Ü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="readingActivity"></a><li><b>readingActivity</b> (default none)<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; readingActivity actifity</code><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; readingActivity aktiv:0:1</code><br>
Erzeugt in den überwachten Geräten das zusätzliche Reading aktiv und versorgt es mit dem Status 0 bzw 1
</li><br>
</ul>
</ul>
<br>
</ul>
=end html_DE
=for :application/json;q=META.json 98_readingsWatcher.pm
{
"abstract": "Module for cyclical watching of readings updates",
"x_lang": {
"de": {
"abstract": "Modul zur zyklische Überwachung von Readings auf Aktualisierung"
}
},
"keywords": [
"readings",
"watch",
"supervision",
"überwachung"
],
"version": "2.1.3",
"release_status": "stable",
"author": [
"Wzut"
],
"x_fhem_maintainer": [
"Wzut"
],
"x_fhem_maintainer_github": [
],
"prereqs": {
"runtime": {
"requires": {
"FHEM": 5.00918799,
"GPUtils": 0,
"Time::HiRes": 0
},
"recommends": {
"FHEM::Meta": 0
},
"suggests": {
}
}
}
}
=end :application/json;q=META.json
=back
=cut