Dead Device Detector

Begonnen von nanostrukturtechniker, 04 Januar 2019, 19:51:07

Vorheriges Thema - Nächstes Thema

nanostrukturtechniker

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&uuml;r tote Ger&auml;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 &lt;name&gt; &lt;MaximumAllowedInterval&gt; &lt;CheckInterval&gt; &lt;device:reading&gt; ...</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 &lt;name&gt; &lt;MaximumAllowedInterval&gt; &lt;CheckInterval&gt; &lt;device:reading&gt; ...</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!


KernSani

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 macht?

Ansonsten auf die Schnelle nur: Es sollte 98_DeadDeviceDetector.pm heissen.

Grüße,

Oli

RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

Christoph Morrison

Speziell für Netzwerkgeräte gibt es auch noch das Nmap-Modul, bei dem es auch ein absent / present-Reading gibt.

nanostrukturtechniker

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