2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

98_WOL: Big update of WOL. Module now uses Blockingcall. By using the new attribute useUdpBroadcast you can specify a Broardcastadress wher the Magicattribute is sent. When using 0 as interal you can switch checking for the device off

git-svn-id: https://svn.fhem.de/fhem/trunk@7623 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
dietmar63 2015-01-18 20:38:44 +00:00
parent 3af5d03f65
commit a5f2aea0f1

View File

@ -22,7 +22,9 @@ package main;
use strict; use strict;
use warnings; use warnings;
use IO::Socket; use IO::Socket;
use Blocking;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw(gettimeofday);
################################################################################ ################################################################################
sub WOL_Initialize($) { sub WOL_Initialize($) {
my ($hash) = @_; my ($hash) = @_;
@ -30,7 +32,8 @@ sub WOL_Initialize($) {
$hash->{SetFn} = "WOL_Set"; $hash->{SetFn} = "WOL_Set";
$hash->{DefFn} = "WOL_Define"; $hash->{DefFn} = "WOL_Define";
$hash->{UndefFn} = "WOL_Undef"; $hash->{UndefFn} = "WOL_Undef";
$hash->{AttrList} = "interval shutdownCmd sysCmd ". $hash->{AttrFn} = "WOL_Attr";
$hash->{AttrList} = "interval shutdownCmd sysCmd useUdpBroadcast ".
$readingFnAttributes; $readingFnAttributes;
} }
################################################################################ ################################################################################
@ -47,32 +50,21 @@ sub WOL_Set($@) {
if ($v eq "on") { if ($v eq "on") {
readingsSingleUpdate($hash, "active", $v, 1); readingsSingleUpdate($hash, "active", $v, 1);
Log3 $hash, 3, "[$name] waking $name with MAC $hash->{MAC} IP $hash->{IP} "; Log3 $hash, 3, "[$name] waking $name with MAC $hash->{MAC} IP $hash->{IP} ";
WOL_GetUpdate( { 'HASH' => $hash } );
} elsif ($v eq "off") { } elsif ($v eq "off") {
readingsSingleUpdate($hash, "active", $v, 1); readingsSingleUpdate($hash, "active", $v, 1);
my $cmd = AttrVal($name, "shutdownCmd", ""); my $cmd = AttrVal($name, "shutdownCmd", "");
if ($cmd eq "") { if ($cmd eq "") {
Log3 $hash, 3, "[$name] no shutdown command given (see shutdownCmd attribute)!"; Log3 $hash, 3, "[$name] no shutdown command given (see shutdownCmd attribute)!";
} else { }
$cmd = SemicolonEscape($cmd); $cmd = SemicolonEscape($cmd);
Log3 $hash, 3, "[$name] shutdownCmd: $cmd executed"; Log3 $hash, 3, "[$name] shutdownCmd: $cmd executed";
my $ret = AnalyzeCommandChain(undef, $cmd); my $ret = AnalyzeCommandChain(undef, $cmd);
Log3 ($hash, 3, "[$name]" . $ret) if($ret); Log3 ($hash, 3, "[$name]" . $ret) if($ret);
return undef;
}
} elsif ($v eq "refresh") { } 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; return undef;
} }
################################################################################ ################################################################################
@ -102,7 +94,7 @@ sub WOL_Define($$) {
if(!($mode =~ m/^(BOTH|EW|UDP)$/)); if(!($mode =~ m/^(BOTH|EW|UDP)$/));
return "invalid repeat<$repeat> - use 999" 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->{NAME} = $name;
$hash->{MAC} = $mac; $hash->{MAC} = $mac;
@ -110,8 +102,6 @@ sub WOL_Define($$) {
$hash->{REPEAT} = $repeat; $hash->{REPEAT} = $repeat;
$hash->{MODE} = $mode; $hash->{MODE} = $mode;
$hash->{INTERVAL} = AttrVal($hash->{NAME}, "interval", 900);
readingsSingleUpdate($hash, "packet_via_EW", "none",0); readingsSingleUpdate($hash, "packet_via_EW", "none",0);
readingsSingleUpdate($hash, "packet_via_UDP", "none",0); readingsSingleUpdate($hash, "packet_via_UDP", "none",0);
readingsSingleUpdate($hash, "state", "none",0); readingsSingleUpdate($hash, "state", "none",0);
@ -119,7 +109,7 @@ sub WOL_Define($$) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
WOL_UpdateReadings($hash); WOL_SetNextTimer($hash, 10);
return undef; return undef;
} }
################################################################################ ################################################################################
@ -127,49 +117,96 @@ sub WOL_Undef($$) {
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
RemoveInternalTimer($hash); myRemoveInternalTimer("ping", $hash);
myRemoveInternalTimer("wake", $hash);
BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
return undef; return undef;
} }
################################################################################ ################################################################################
sub WOL_UpdateReadings($) { 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) = @_; 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"; Log3 $hash->{NAME}, 3, "BlockingCall for ".$hash->{NAME}." was aborted";
my $res = qx ($ping); WOL_SetNextTimer($hash);
$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);
} }
################################################################################ ################################################################################
sub WOL_GetUpdate($) { 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"); my $active = ReadingsVal($hash->{NAME}, "active", "nF");
if ($active eq "on") { if ($active eq "on") {
WOL_wake($hash); WOL_wake($hash);
if ($hash->{REPEAT} > 0) { 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 $name = $hash->{NAME};
my $mac = $hash->{MAC}; my $mac = $hash->{MAC};
my $host = $hash->{IP}; my $host = $hash->{IP};
$host = '255.255.255.255' if (!defined $host);
$host = AttrVal($name, "useUdpBroadcast",$host);
readingsBeginUpdate ($hash); readingsBeginUpdate ($hash);
Log3 $hash, 4, "[$name] keeping $name with MAC $mac IP $host busy"; 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); WOL_by_ew ($hash, $mac);
readingsBulkUpdate ($hash, "packet_via_EW", $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); WOL_by_udp ($hash, $mac, $host);
readingsBulkUpdate ($hash, "packet_via_UDP", $host); readingsBulkUpdate ($hash, "packet_via_UDP", $host);
} }
readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); 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 { sub WOL_by_udp {
my ($hash, $mac_addr, $host, $port) = @_; my ($hash, $mac_addr, $host, $port) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
# use the discard service if $port not passed in # 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 : $!"; my $sock = new IO::Socket::INET(Proto=>'udp') or die "socket : $!";
if(!$sock) { if(!$sock) {
Log3 $hash, 1, "[$name] Can't create WOL socket"; Log3 $hash, 1, "[$name] Can't create WOL socket";
return 1; return 1;
} }
$host = '192.168.2.255';
my $ip_addr = inet_aton($host); my $ip_addr = inet_aton($host);
my $sock_addr = sockaddr_in($port, $ip_addr); my $sock_addr = sockaddr_in($port, $ip_addr);
$mac_addr =~ s/://g; $mac_addr =~ s/://g;
@ -254,6 +294,45 @@ sub WOL_by_ew($$) {
return 1; 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; 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 wol shutdownCmd "/bin/echo "Teatime" > /dev/console" # shell command
</PRE> </PRE>
<li><code>attr &lt;name&gt; interval &lt;seconds&gt;</code></a> <li><code>attr &lt;name&gt; interval &lt;seconds&gt;</code></a>
<br>defines the time between two checks by a <i>ping</i> if state of &lt;name&gt is <i>on</i></li> <br>defines the time between two checks by a <i>ping</i> if state of &lt;name&gt is <i>on</i> By using 0 as parametr for interval you can switch off checking the device.</li>
<li><code>attr &lt;name&gt; useUdpBroadcast &lt;<broardcastAdress>&gt;</code>
<br>When using UDP then the magic packet can be send to one of the broardcastAdresses (x.x.x.255, x.x.255.255, x.255.255.255) instead of the target host address.
Try using this, when you are trying to wake up a machine in your own subnet and the wakekup with the target adress is instable or doesn't work.</li>
</ul> </ul>
</ul> </ul>