Hallo zusammen,
weil meine Unitymedia Connect Box gerne einmal Geräte aus dem WLan schmeißt und ich keine Lust habe, von Hand nachzuschauen, habe ich ein kleines Modul geschrieben, das mir ein Event generiert, sobald eines der Gerät sich nicht innerhalb des vorgegebenen Intervalls meldet. Mit einem Dummy erfüllt es zumindest schon mal den Zweck:
package main;
use strict;
use warnings;
my %DeadDeviceDetector_gets = (
"" => ""
);
sub DeadDeviceDetector_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'DeadDeviceDetector_Define';
$hash->{UndefFn} = 'DeadDeviceDetector_Undef';
$hash->{SetFn} = 'DeadDeviceDetector_Set';
$hash->{GetFn} = 'DeadDeviceDetector_Get';
$hash->{AttrFn} = 'DeadDeviceDetector_Attr';
$hash->{ReadFn} = 'DeadDeviceDetector_Read';
$hash->{AttrList} =
"active:yes,no "
. $readingFnAttributes;
}
sub DeadDeviceDetector_Define($$) {
my ($hash, $def) = @_;
my @param = split('[ \t]+', $def);
if(int(@param) < 4) {
return "too few parameters: define <name> DeadDeviceDetector <MaxTimeInS> <CheckInterval> <device:reading> [device:reading] ...";
}
$hash->{Name} = $param[0];
$hash->{TimeInS} = $param[2];
$hash->{CheckInterval} = $param[3];
#Collect all the devices and readings
$hash->{AmountOfDevices} = scalar(@param) - 4;
for (my $i=4; $i < scalar(@param) ; $i++)
{
# Differ by reading and device
my @s = split(':', $param[$i]);
$hash->{DeviceName}{"device" . ($i-4)} = $s[0];
$hash->{DeviceReading}{"reading" . ($i-4)} = $s[1];
}
DeadDeviceDetector_StartTimer($hash);
return undef;
}
sub DeadDeviceDetector_Undef($$) {
my ($hash, $arg) = @_;
# Clean timer
RemoveInternalTimer($hash);
return undef;
}
sub DeadDeviceDetector_Get($@) {
my ($hash, @param) = @_;
return '"get DeadDeviceDetector" needs at least one argument' if (int(@param) < 2);
my $name = shift @param;
my $opt = shift @param;
if(!$DeadDeviceDetector_gets{$opt}) {
my @cList = keys %DeadDeviceDetector_gets;
return "Unknown argument $opt, choose one of " . join(" ", @cList);
}
if($attr{$name}{formal} eq 'yes') {
return $DeadDeviceDetector_gets{$opt}.', sir';
}
return $DeadDeviceDetector_gets{$opt};
}
sub DeadDeviceDetector_Set($@) {
my ($hash, @param) = @_;
return '"set DeadDeviceDetector" needs at least one argument' if (int(@param) < 2);
my $name = shift @param;
my $opt = shift @param;
my $value = join("", @param);
if(!defined($DeadDeviceDetector_gets{$opt})) {
my @cList = keys %DeadDeviceDetector_gets;
return "Unknown argument $opt, choose one of " . join(" ", @cList);
}
#We have only one argument atm.
if ($value eq "active")
{
$hash->{active} = $value;
RemoveInternalTimer($hash);
if ($hash->{active}=="On")
{
DeadDeviceDetector_StartTimer($hash);
}else
{
# We just remove the timer
}
}
return "$opt set to $value.";
}
sub DeadDeviceDetector_Timer($@)
{
my ($hash, $name, @param) = @_;
# We get our values.
my @FailedDevices;
for (my $i=0; $i < $hash->{AmountOfDevices} ; $i++)
{
my $LastUpdateWasSago = time - time_str2num(ReadingsTimestamp($hash->{DeviceName}{"device" . $i},$hash->{DeviceReading}{"reading" . $i}, time)) ;
my $UpdateTime = $hash->{TimeInS};
#Log3 $name, 3, "Got Device ".$hash->{DeviceName}{"device" . $i}." with reading " . $hash->{DeviceReading}{"reading" . $i}." updated before ".$LastUpdateWasSago."s, it should update at least after ".$UpdateTime."s";
if ($LastUpdateWasSago >$UpdateTime )
{
#OK, device did not update :-(
# We update the command
my $ActDevice = $hash->{DeviceName}{"device" . $i};
#Log3 $name, 3, $ActDevice . " did not update since ".$LastUpdateWasSago."s";
push @FailedDevices, $ActDevice;
}
}
my $FailedDevicesCollection = join(", ", @FailedDevices);
# Tell the world about it, but not, if we have an empty string, the user wants to know only about failed stuff.
readingsBeginUpdate($hash);
readingsBulkUpdateIfChanged($hash, "state", $FailedDevicesCollection);
if ($FailedDevicesCollection ne "")
{
readingsEndUpdate($hash, 1);
}
else
{
readingsEndUpdate($hash, 0);
}
# Restart timer
DeadDeviceDetector_StartTimer($hash);
}
sub DeadDeviceDetector_StartTimer($@) {
my ($hash, $name, @param) = @_;
RemoveInternalTimer($hash);
InternalTimer(gettimeofday()+$hash->{CheckInterval}, "DeadDeviceDetector_Timer", $hash, 0);
}
sub DeadDeviceDetector_Attr(@) {
my ($cmd,$name,$attr_name,$attr_value) = @_;
if($cmd eq "set") {
if($attr_name eq "active") {
if($attr_value !~ /^yes|no$/) {
my $err = "Invalid argument $attr_value to $attr_name. Must be yes or no.";
Log 3, "DeadDeviceDetector: ".$err;
return $err;
}
} else {
#return "Unknown attr $attr_name";
}
}
return undef;
}
1;
=pod
=item helper
=item summary dead device detector
=item summary_DE Detektor für tote Geräte
=begin html
<a name="DeadDeviceDetector"></a>
<h3>DeadDeviceDetector</h3>
<ul>
Defines a detector for dead devices (devices which are not updated in a certain interval).
<br><br>
<a name="DeadDeviceDetectordefine"></a>
<b>Define</b>
<ul>
<code>define <name> <MaximumAllowedInterval> <CheckInterval> <device:reading> ...</code>
<br><br>
Example:
<ul>
<code>define ddd DeadDeviceDetector 20 5 DeviceToCheck:ImportantReading</code><br>
</ul>
</ul>
<br>
<a name="DeadDeviceDetectorattr"></a>
<b>Attributes</b>
<ul>
<li><a href="#active">active</a></li>
not yet implemented
</ul>
<br>
</ul>
=end html
=begin html_DE
<a name="DeadDeviceDetector"></a>
<h3>DeadDeviceDetector</h3>
<ul>
Definiert eine Erkennung für tote Geräte (Geräte, deren Reading nicht in einem bestimmten Intervall erneuert wurde).
<br><br>
<a name="DeadDeviceDetectordefine"></a>
<b>Define</b>
<ul>
<code>define <name> <MaximumAllowedInterval> <CheckInterval> <device:reading> ...</code>
<br><br>
Beispiel:
<ul>
<code>define ddd DeadDeviceDetector 20 5 DeviceToCheck:ImportantReading</code><br>
</ul>
</ul>
<br>
<a name="DeadDeviceDetectorattr"></a>
<b>Attributes</b>
<ul>
<li><a href="#active">active</a></li>
Noch nicht implementiert.
</ul>
<br>
</ul>
=end html_DE
=cut
Zur Verwendung: Einfach in 98_DeadDeviceDetector.pl ins FHEM Verzeichnis kopieren.
Wenn ihr noch Ergänzungen und Tipps habt: Her damit!
Hi nanostrukturtechniker,
ich habe mir das Coding jetzt nicht im Detail angesehen, aber meinem Verständnis nach, macht das Ganze in etwa das, was das Monitoring Modul (https://fhem.de/commandref.html#monitoring) macht?
Ansonsten auf die Schnelle nur: Es sollte 98_DeadDeviceDetector.pm heissen.
Grüße,
Oli
Speziell für Netzwerkgeräte gibt es auch noch das Nmap-Modul, bei dem es auch ein absent / present-Reading gibt.
Hallo,
stimmt, da habt ihr natürlich Recht, damit geht es auch. Hatte das Modul nur noch nicht gefunden.
Dafür ist der DeadDeviceDetector halt ein bisschen kleiner und übersichtlicher ;-) Und ich habe mal wieder meine Perl Kenntnisse aufgefrischt. Kommt auch alle paar Jahre mal wieder vor :-)
Viele Grüße
Jörg