diff --git a/FHEM/98_WOL.pm b/FHEM/98_WOL.pm index 30973f8cf..9d2091698 100644 --- a/FHEM/98_WOL.pm +++ b/FHEM/98_WOL.pm @@ -22,7 +22,9 @@ package main; use strict; use warnings; use IO::Socket; +use Blocking; use Time::HiRes qw(gettimeofday); + ################################################################################ sub WOL_Initialize($) { my ($hash) = @_; @@ -30,7 +32,8 @@ sub WOL_Initialize($) { $hash->{SetFn} = "WOL_Set"; $hash->{DefFn} = "WOL_Define"; $hash->{UndefFn} = "WOL_Undef"; - $hash->{AttrList} = "interval shutdownCmd sysCmd ". + $hash->{AttrFn} = "WOL_Attr"; + $hash->{AttrList} = "interval shutdownCmd sysCmd useUdpBroadcast ". $readingFnAttributes; } ################################################################################ @@ -47,32 +50,21 @@ sub WOL_Set($@) { if ($v eq "on") { readingsSingleUpdate($hash, "active", $v, 1); Log3 $hash, 3, "[$name] waking $name with MAC $hash->{MAC} IP $hash->{IP} "; + WOL_GetUpdate( { 'HASH' => $hash } ); } elsif ($v eq "off") { readingsSingleUpdate($hash, "active", $v, 1); my $cmd = AttrVal($name, "shutdownCmd", ""); if ($cmd eq "") { Log3 $hash, 3, "[$name] no shutdown command given (see shutdownCmd attribute)!"; - } else { - $cmd = SemicolonEscape($cmd); - Log3 $hash, 3, "[$name] shutdownCmd: $cmd executed"; - my $ret = AnalyzeCommandChain(undef, $cmd); - Log3 ($hash, 3, "[$name]" . $ret) if($ret); - return undef; - } + } + $cmd = SemicolonEscape($cmd); + Log3 $hash, 3, "[$name] shutdownCmd: $cmd executed"; + my $ret = AnalyzeCommandChain(undef, $cmd); + Log3 ($hash, 3, "[$name]" . $ret) if($ret); } elsif ($v eq "refresh") { - ; + WOL_UpdateReadings( { 'HASH' => $hash } ); } - WOL_UpdateReadings($hash); - return undef if($v eq "refresh"); - - RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+60, "WOL_UpdateReadings", $hash, 0); - - my $active = ReadingsVal($hash->{NAME}, "active", "nF"); - if ($active eq "on") { - WOL_GetUpdate($hash); - } return undef; } ################################################################################ @@ -102,7 +94,7 @@ sub WOL_Define($$) { if(!($mode =~ m/^(BOTH|EW|UDP)$/)); return "invalid repeat<$repeat> - use 999" - if(!($repeat =~ m/^[0-9]{3,3}$/i)); + if(!($repeat =~ m/^[0-9]{1,3}$/i)); $hash->{NAME} = $name; $hash->{MAC} = $mac; @@ -110,8 +102,6 @@ sub WOL_Define($$) { $hash->{REPEAT} = $repeat; $hash->{MODE} = $mode; - $hash->{INTERVAL} = AttrVal($hash->{NAME}, "interval", 900); - readingsSingleUpdate($hash, "packet_via_EW", "none",0); readingsSingleUpdate($hash, "packet_via_UDP", "none",0); readingsSingleUpdate($hash, "state", "none",0); @@ -119,7 +109,7 @@ sub WOL_Define($$) { RemoveInternalTimer($hash); - WOL_UpdateReadings($hash); + WOL_SetNextTimer($hash, 10); return undef; } ################################################################################ @@ -127,49 +117,96 @@ sub WOL_Undef($$) { my ($hash, $arg) = @_; - RemoveInternalTimer($hash); + myRemoveInternalTimer("ping", $hash); + myRemoveInternalTimer("wake", $hash); + BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID})); return undef; } ################################################################################ sub WOL_UpdateReadings($) { + my ($myHash) = @_; + + my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); + return if (!defined($hash)); + my $name = $hash->{NAME}; + + + my $blockingFn = "WOL_Ping"; + my $arg = $hash->{NAME}."|".$hash->{IP}; + my $finishFn = "WOL_PingDone"; + my $timeout = 3; + my $abortFn = "WOL_PingAbort"; + + if (!(exists($hash->{helper}{RUNNING_PID}))) { + $hash->{helper}{RUNNING_PID} = + BlockingCall($blockingFn, $arg, $finishFn, $timeout, $abortFn, $hash); + } else { + Log3 $hash, 3, "[$name] Blocking Call running no new started"; + WOL_SetNextTimer($hash); + } +} +################################################################################ +sub WOL_Ping($){ + my ($string) = @_; + my ($name, $ip) = split("\\|", $string); + my $hash = $defs{$name}; + + my $ping = "ping -c 1 -w 2 $ip"; + my $res = qx ($ping); + $res = "" if (!defined($res)); + Log3 $hash, 5, "[$name] executing: $ping"; + + my $erreichbar = !($res =~ m/100%/); + my $return = "$name|$erreichbar"; + return $return; +} +################################################################################ +sub WOL_PingDone($){ + my ($string) = @_; + my ($name, $erreichbar) = split("\\|", $string); + my $hash = $defs{$name}; + + readingsBeginUpdate ($hash); + + if ($erreichbar) { + Log3 $hash, 5, "[$name] ping succesful - state = on"; + readingsBulkUpdate ($hash, "isRunning", "true"); + readingsBulkUpdate ($hash, "state", "on"); + } else { + Log3 $hash, 5, "[$name] ping not succesful - state = off"; + readingsBulkUpdate ($hash, "isRunning", "false"); + readingsBulkUpdate ($hash, "state", "off"); + } + + readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); + + delete($hash->{helper}{RUNNING_PID}); + + WOL_SetNextTimer($hash); +} +################################################################################ +sub WOL_PingAbort($){ my ($hash) = @_; - $hash->{INTERVAL} = AttrVal($hash->{NAME}, "interval", 900); - - my $name = $hash->{NAME}; - my $ip = $hash->{IP}; - readingsBeginUpdate ($hash); + delete($hash->{helper}{RUNNING_PID}); - my $ping = "ping -c 1 -w 2 $ip"; - my $res = qx ($ping); - $res = "" if (!defined($res)); - - Log3 $hash, 5, "[$name] executing: $ping"; - Log3 $hash, 5, "[$name] result of ping:$res"; - - if ( $res =~ m/100%/) { - Log3 $hash, 5, "[$name] ping not succesful - state = on"; - readingsBulkUpdate ($hash, "isRunning", "false"); - readingsBulkUpdate ($hash, "state", "off"); - } else { - Log3 $hash, 5, "[$name] ping succesful - state = on"; - readingsBulkUpdate ($hash, "isRunning", "true"); - readingsBulkUpdate ($hash, "state", "on"); - } - - readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_UpdateReadings", $hash, 0); + Log3 $hash->{NAME}, 3, "BlockingCall for ".$hash->{NAME}." was aborted"; + WOL_SetNextTimer($hash); } ################################################################################ sub WOL_GetUpdate($) { - my ($hash) = @_; + my ($myHash) = @_; + + my $hash = myGetHashIndirekt($myHash, (caller(0))[3]); + return if (!defined($hash)); my $active = ReadingsVal($hash->{NAME}, "active", "nF"); if ($active eq "on") { WOL_wake($hash); if ($hash->{REPEAT} > 0) { - InternalTimer(gettimeofday()+$hash->{REPEAT}, "WOL_GetUpdate", $hash, 0); + myRemoveInternalTimer("wake", $hash); + myInternalTimer ("wake", gettimeofday()+$hash->{REPEAT}, "WOL_GetUpdate", $hash, 0); } } @@ -180,37 +217,40 @@ sub WOL_wake($){ my $name = $hash->{NAME}; my $mac = $hash->{MAC}; my $host = $hash->{IP}; - + + $host = '255.255.255.255' if (!defined $host); + $host = AttrVal($name, "useUdpBroadcast",$host); + readingsBeginUpdate ($hash); Log3 $hash, 4, "[$name] keeping $name with MAC $mac IP $host busy"; - if ($hash->{MODE} eq "BOTH" || $hash->{MODE} eq "EW" ) { + if ($hash->{MODE} ~~ ["BOTH", "EW" ] ) { WOL_by_ew ($hash, $mac); readingsBulkUpdate ($hash, "packet_via_EW", $mac); } - if ($hash->{MODE} eq "BOTH" || $hash->{MODE} eq "UDP" ) { + if ($hash->{MODE} ~~ ["BOTH", "UDP"] ) { WOL_by_udp ($hash, $mac, $host); readingsBulkUpdate ($hash, "packet_via_UDP", $host); } readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); } ################################################################################ -# method to wakevia lan, taken from Net::Wake package +# method to wake via lan, taken from Net::Wake package sub WOL_by_udp { my ($hash, $mac_addr, $host, $port) = @_; my $name = $hash->{NAME}; # use the discard service if $port not passed in - if (! defined $host) { $host = '255.255.255.255' } - if (! defined $port || $port !~ /^\d+$/ ) { $port = 9 } + if (!defined $port || $port !~ /^\d+$/ ) { $port = 9 } my $sock = new IO::Socket::INET(Proto=>'udp') or die "socket : $!"; if(!$sock) { Log3 $hash, 1, "[$name] Can't create WOL socket"; return 1; } - + + $host = '192.168.2.255'; my $ip_addr = inet_aton($host); my $sock_addr = sockaddr_in($port, $ip_addr); $mac_addr =~ s/://g; @@ -254,6 +294,45 @@ sub WOL_by_ew($$) { return 1; } +################################################################################ +sub WOL_SetNextTimer($;$) { + my ($hash, $int) = @_; + + my $name = $hash->{NAME}; + + $int = AttrVal($hash->{NAME}, "interval", 900) if not defined $int; + if ($int != 0) { + Log3 $hash, 5, "[$name] WOL_SetNextTimer to $int"; + myRemoveInternalTimer("ping", $hash); + myInternalTimer ("ping", gettimeofday() + $int, "WOL_UpdateReadings", $hash, 0); + } + return; +} +################################################################################ +sub WOL_Attr($$$) { + my ($cmd, $name, $attrName, $attrVal) = @_; + + my $hash = $defs{$name}; + + if ($attrName eq "useUdpBroadcast") { + if(!($attrVal =~ m/^([0-9]{1,3}([.-]|$)){4}$/i)) { + Log3 $hash, 3, "[$name] invalid Broadcastadress<$attrVal> - use ddd.ddd.ddd.ddd"; + } + } + + if ($attrName eq "interval") { + RemoveInternalTimer($hash); + + # when deleting the interval we trigger an update in one second + my $int = ($cmd eq "del") ? 1 : $attrVal; + if ($int != 0) { + # restart timer with new interval + WOL_SetNextTimer($hash, $int); + } + } + + return undef; +} 1; @@ -338,7 +417,10 @@ So, for example a Buffalo NAS can be kept awake. attr wol shutdownCmd "/bin/echo "Teatime" > /dev/console" # shell command
attr <name> interval <seconds>
- attr <name> useUdpBroadcast <>
+