mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-18 18:16:03 +00:00
added a parameter to choose the rule for waking up the host EW|UDP
added an other parameter to send the magic packet repeatedly, by keeping a NAS alive(Buffalo). git-svn-id: https://svn.fhem.de/fhem/trunk@4056 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
f6347c3f46
commit
5d3aa150a0
@ -25,6 +25,10 @@
|
||||
# define t4 RandomTimer *23:01:40 Zirkulation 23:02:40 100; attr t4 verbose 5;
|
||||
#
|
||||
##############################################################################
|
||||
# 10.09.2013 Svenson : disable direct if attribute changed, add state disabled;
|
||||
# randomtimer run every day if attribut runonce 0 (default is 1)
|
||||
#
|
||||
##############################################################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
@ -38,7 +42,8 @@ sub RandomTimer_Initialize($)
|
||||
|
||||
$hash->{DefFn} = "RandomTimer_Define";
|
||||
$hash->{UndefFn} = "RandomTimer_Undef";
|
||||
$hash->{AttrList} = "onCmd offCmd switchmode disable:0,1 disableCond ".
|
||||
$hash->{AttrFn} = "RandomTimer_Attr";
|
||||
$hash->{AttrList} = "onCmd offCmd switchmode disable:0,1 disableCond runonce:0,1 ".
|
||||
$readingFnAttributes;
|
||||
}
|
||||
#
|
||||
@ -187,6 +192,9 @@ sub RandomTimer_Exec($)
|
||||
my $midnight = $now1 + 24*3600 - (3600*$hour + 60*$min + $sec);
|
||||
Log3 $hash, 4, "[".$hash->{NAME}. "]"." Next Timer ".strftime("%d.%m.%Y %H:%M:%S",localtime($midnight));
|
||||
InternalTimer($midnight, "RandomTimer_ExecRepeater_verzoegert", $hash, 0);
|
||||
} else {
|
||||
$hash->{COMMAND} = "off";
|
||||
$hash->{STATE} = "disabled";
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -211,10 +219,14 @@ sub RandomTimer_Exec($)
|
||||
$hash->{STATE} = "off";
|
||||
fhem ("set $hash->{DEVICE} $hash->{COMMAND}");
|
||||
delete $hash->{ABSCHALTZEIT};
|
||||
|
||||
if ($hash->{REP} gt "") {
|
||||
RandomTimer_ExecRepeater($hash);
|
||||
} else {
|
||||
fhem ("delete $hash->{NAME}");
|
||||
if ( AttrVal($hash->{NAME}, "runonce", 1) == 1 )
|
||||
{ fhem ("delete $hash->{NAME}") ;}
|
||||
else
|
||||
{ RandomTimer_ExecRepeater($hash);}
|
||||
}
|
||||
} else {
|
||||
toggleDevice($hash);
|
||||
@ -225,6 +237,25 @@ sub RandomTimer_Exec($)
|
||||
#
|
||||
#
|
||||
#
|
||||
sub RandomTimer_Attr($$$) {
|
||||
my ($cmd, $name, $attrName, $attrVal) = @_;
|
||||
|
||||
if( $attrName eq "disable" ) {
|
||||
my $hash = $defs{$name};
|
||||
if( $cmd eq "set" && $attrVal ne "0" ) {
|
||||
$attr{$name}{$attrName} = 1;
|
||||
$hash->{STATE} = "disabled";
|
||||
RemoveInternalTimer($hash);
|
||||
} else {
|
||||
$attr{$name}{$attrName} = 0;
|
||||
$hash->{STATE} = "off";
|
||||
RandomTimer_ExecRepeater($hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
#
|
||||
#
|
||||
#
|
||||
sub RandomTimer_ExecRepeater_verzoegert($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
@ -1,5 +1,22 @@
|
||||
##############################################
|
||||
# $Id$
|
||||
# $Id
|
||||
# erweitert um die Funktion nas_control Dietmar Ortmann $
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
@ -7,87 +24,105 @@ use warnings;
|
||||
use IO::Socket;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
sub
|
||||
WOL_Initialize($)
|
||||
sub WOL_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{SetFn} = "WOL_Set";
|
||||
$hash->{DefFn} = "WOL_Define";
|
||||
$hash->{UndefFn} = "WOL_Undef";
|
||||
$hash->{AttrList} = "shutdownCmd loglevel:0,1,2,3,4,5,6";
|
||||
$hash->{AttrList} = "interval shutdownCmd ".
|
||||
$readingFnAttributes;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
WOL_Set($@)
|
||||
#
|
||||
#
|
||||
#
|
||||
sub WOL_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "no set value specified" if(int(@a) < 2);
|
||||
return "Unknown argument $a[1], choose one of refresh on" if($a[1] eq "?");
|
||||
|
||||
return "Unknown argument $a[1], choose one of on off refresh" if($a[1] eq "?");
|
||||
|
||||
my $name = shift @a;
|
||||
my $v = join(" ", @a);
|
||||
my $logLevel = GetLogLevel($name,2);
|
||||
|
||||
Log $logLevel, "WOL set $name $v";
|
||||
Log3 $hash, 3, "WOL set $name $v";
|
||||
|
||||
if($v eq "on")
|
||||
{
|
||||
eval {
|
||||
for(my $i = 1; $i <= 3; $i++) {
|
||||
wake($hash, $logLevel);
|
||||
}
|
||||
};
|
||||
if ($@){
|
||||
### catch block
|
||||
Log $logLevel, "WOL error: $@";
|
||||
};
|
||||
Log $logLevel, "WOL waking $name";
|
||||
|
||||
} elsif ($v eq "refresh")
|
||||
{
|
||||
WOL_GetUpdate($hash);
|
||||
if ($v eq "on") {
|
||||
$hash->{STATE} = $v;
|
||||
Log3 $hash, 3, "WOL waking $name with MAC $hash->{MAC} IP $hash->{IP} ";
|
||||
} elsif ($v eq "off") {
|
||||
my $cmd = AttrVal($name, "shutdownCmd", "");
|
||||
if ($cmd eq "")
|
||||
{
|
||||
Log $logLevel, "No shutdown command given!";
|
||||
return "no shutdown command given (see shutdownCmd attribute)!"
|
||||
}
|
||||
`$cmd`;
|
||||
} else
|
||||
{
|
||||
return "unknown argument $v, choose one of refresh, on";
|
||||
$hash->{STATE} = $v;
|
||||
my $cmd = AttrVal($name, "shutdownCmd", "");
|
||||
if ($cmd eq "") {
|
||||
Log3 $hash, 3, "No shutdown command given (see shutdownCmd attribute)!";
|
||||
} else {
|
||||
qx ($cmd);
|
||||
}
|
||||
} elsif ($v eq "refresh") {
|
||||
;
|
||||
}
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
InternalTimer(gettimeofday()+5, "WOL_UpdateReadings", $hash, 0);
|
||||
|
||||
if ($hash->{STATE} eq "on") {
|
||||
InternalTimer(gettimeofday()+1, "WOL_GetUpdate", $hash, 0);
|
||||
}
|
||||
|
||||
$hash->{CHANGED}[0] = $v;
|
||||
$hash->{STATE} = $v;
|
||||
$hash->{READINGS}{state}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{state}{VAL} = $v;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
WOL_Define($$)
|
||||
#
|
||||
#
|
||||
#
|
||||
sub WOL_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
my $u = "wrong syntax: define <name> WOL MAC_ADRESS IP";
|
||||
my $u = "wrong syntax: define <name> WOL <MAC_ADRESS> <IP> <mode> <repeat> ";
|
||||
return $u if(int(@a) < 4);
|
||||
|
||||
$hash->{MAC} = $a[2];
|
||||
$hash->{IP} = $a[3];
|
||||
$hash->{INTERVAL} = 600;
|
||||
|
||||
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_GetUpdate", $hash, 0);
|
||||
my $name = shift @a;
|
||||
my $type = shift @a;
|
||||
my $mac = shift @a;
|
||||
my $ip = shift @a;
|
||||
my $mode = shift @a;
|
||||
my $repeat = shift @a;
|
||||
|
||||
$repeat = "000" if (!defined $repeat);
|
||||
$mode = "BOTH" if (!defined $mode);
|
||||
|
||||
return "invalid MAC<$mac> - use HH:HH:HH:HH:HH"
|
||||
if(!($mac =~ m/^([0-9a-f]{2}([:-]|$)){6}$/i ));
|
||||
|
||||
return "invalid IP<$ip> - use ddd.ddd.ddd.ddd"
|
||||
if(!($ip =~ m/^([0-9]{1,3}([.-]|$)){4}$/i ));
|
||||
|
||||
return "invalid mode<$mode> - use EW|UDP|BOTH"
|
||||
if(!($mode =~ m/^(BOTH|EW|UDP)$/));
|
||||
|
||||
return "invalid repeat<$repeat> - use 999"
|
||||
if(!($repeat =~ m/^[0-9]{3,3}$/i));
|
||||
|
||||
$hash->{NAME} = $name;
|
||||
$hash->{MAC} = $mac;
|
||||
$hash->{IP} = $ip;
|
||||
$hash->{REPEAT} = $repeat;
|
||||
$hash->{MODE} = $mode;
|
||||
|
||||
$hash->{INTERVAL} = AttrVal($hash->{NAME}, "interval", 900);
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
InternalTimer(gettimeofday()+5, "WOL_UpdateReadings", $hash, 0);
|
||||
InternalTimer(gettimeofday()+30,"WOL_GetUpdate", $hash, 0);
|
||||
|
||||
readingsSingleUpdate($hash, "packet_via_EW", "none",0);
|
||||
readingsSingleUpdate($hash, "packet_via_UDP", "none",0);
|
||||
return undef;
|
||||
}
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
sub WOL_Undef($$) {
|
||||
|
||||
my ($hash, $arg) = @_;
|
||||
@ -95,123 +130,182 @@ sub WOL_Undef($$) {
|
||||
RemoveInternalTimer($hash);
|
||||
return undef;
|
||||
}
|
||||
#
|
||||
#
|
||||
#
|
||||
sub WOL_UpdateReadings($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
$hash->{INTERVAL} = AttrVal($hash->{NAME}, "interval", 900);
|
||||
|
||||
my $ip = $hash->{IP};
|
||||
|
||||
readingsBeginUpdate ($hash);
|
||||
|
||||
if (`ping -c 1 -w 2 $ip` =~ m/100%/) {
|
||||
readingsBulkUpdate ($hash, "isRunning", "false");
|
||||
} else {
|
||||
readingsBulkUpdate ($hash, "isRunning", "true");
|
||||
}
|
||||
|
||||
readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1));
|
||||
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_UpdateReadings", $hash, 0);
|
||||
}
|
||||
#
|
||||
#
|
||||
#
|
||||
sub WOL_GetUpdate($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
my $ip = $hash->{IP};
|
||||
#if (system("ping -q -c 1 $ip > /dev/null") == 0)
|
||||
####
|
||||
#changed 2013-07-27 by UliM
|
||||
#based on Thread http://forum.fhem.de/index.php?t=msg&th=11823&goto=69801&rid=86#msg_69801
|
||||
#if (`ping -c 1 $ip` =~ m/100/)
|
||||
if (`ping -c 1 -w 1 $ip` =~ m/100%/)
|
||||
{
|
||||
$hash->{READINGS}{state}{VAL} = "off";
|
||||
$hash->{READINGS}{isRunning}{VAL} = "false";
|
||||
} else
|
||||
{
|
||||
$hash->{READINGS}{state}{VAL} = "on";
|
||||
$hash->{READINGS}{isRunning}{VAL} = "true";
|
||||
|
||||
if ($hash->{STATE} eq "on") {
|
||||
wake($hash);
|
||||
}
|
||||
$hash->{READINGS}{state}{TIME} = TimeNow();
|
||||
$hash->{READINGS}{isRunning}{TIME} = TimeNow();
|
||||
|
||||
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WOL_GetUpdate", $hash, 0);
|
||||
}
|
||||
|
||||
sub wake
|
||||
if ($hash->{REPEAT} > 0 && $hash->{STATE} eq "on" ) {
|
||||
InternalTimer(gettimeofday()+$hash->{REPEAT}, "WOL_GetUpdate", $hash, 0);
|
||||
}
|
||||
}
|
||||
#
|
||||
#
|
||||
#
|
||||
sub wake($)
|
||||
{
|
||||
my ($hash, $logLevel) = @_;
|
||||
my $mac = $hash->{MAC};
|
||||
|
||||
Log $logLevel, "trying to wake $mac";
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $mac = $hash->{MAC};
|
||||
my $host = $hash->{IP};
|
||||
|
||||
my $response = `/usr/bin/ether-wake $mac`;
|
||||
Log $logLevel, "trying etherwake with response: $response";
|
||||
readingsBeginUpdate ($hash);
|
||||
|
||||
wol_by_udp($mac);
|
||||
Log $logLevel, "trying direct socket via UDP";
|
||||
Log3 $hash, 3, "WOL keeping $name with MAC $mac IP $host busy";
|
||||
|
||||
if ($hash->{MODE} eq "BOTH" || $hash->{MODE} eq "EW" ) {
|
||||
wol_by_ew ($mac);
|
||||
readingsBulkUpdate ($hash, "packet_via_EW", $mac);
|
||||
}
|
||||
if ($hash->{MODE} eq "BOTH" || $hash->{MODE} eq "UDP" ) {
|
||||
wol_by_udp ($hash, $mac, $host);
|
||||
readingsBulkUpdate ($hash, "packet_via_UDP", $host);
|
||||
}
|
||||
readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1));
|
||||
}
|
||||
|
||||
# method to wake via lan, taken from Net::Wake package
|
||||
#
|
||||
#
|
||||
# method to wakevia lan, taken from Net::Wake package
|
||||
sub wol_by_udp {
|
||||
my ($mac_addr, $host, $port) = @_;
|
||||
my ($hash, $mac_addr, $host, $port) = @_;
|
||||
|
||||
# use the discard service if $port not passed in
|
||||
if (! defined $host) { $host = '255.255.255.255' }
|
||||
if (! defined $port || $port !~ /^\d+$/ ) { $port = 9 }
|
||||
|
||||
my $sock = new IO::Socket::INET(Proto=>'udp') or die "socket : $!";
|
||||
die "Can't create WOL socket" if(!$sock);
|
||||
|
||||
my $ip_addr = inet_aton($host);
|
||||
if(!$sock) {
|
||||
Log3 $hash, 1, "Can't create WOL socket";
|
||||
return 1;
|
||||
}
|
||||
|
||||
my $ip_addr = inet_aton($host);
|
||||
my $sock_addr = sockaddr_in($port, $ip_addr);
|
||||
$mac_addr =~ s/://g;
|
||||
my $packet = pack('C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16);
|
||||
$mac_addr =~ s/://g;
|
||||
my $packet = pack('C6H*', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, $mac_addr x 16);
|
||||
|
||||
setsockopt($sock, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
|
||||
|
||||
send($sock, $packet, 0, $sock_addr) or die "send : $!";
|
||||
close ($sock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#
|
||||
#
|
||||
# method to wakevia ether-wake
|
||||
sub wol_by_ew {
|
||||
my ($mac) = @_;
|
||||
|
||||
my $response = `/usr/bin/ether-wake $mac`;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="WOL"></a>
|
||||
<h3>WOL</h3>
|
||||
|
||||
Defines a WOL device via its MAC and IP address.<br><br>
|
||||
|
||||
when sending the <b>on</b> command to a WOL device it wakes up the dependent device by sending a magic packet. When running in repeat mode the magic paket ist sent every n seconds to the device.
|
||||
So, for example a Buffalo NAS can be kept awake.
|
||||
<ul>
|
||||
<a name="WOLdefine"></a>
|
||||
<h4>Define</h4>
|
||||
<ul>
|
||||
<code>define <name> WOL <MAC> <IP>
|
||||
<unitcode></code>
|
||||
<code><b><font size="+1">define <name> WOL <MAC> <IP> [<mode> [<repeat>]]</font></b></code>
|
||||
<br><br>
|
||||
|
||||
Defines a WOL device via its MAC and IP address.<br><br>
|
||||
<dl>
|
||||
<dt><b>MAC</b></dt>
|
||||
<dd>MAC-Adress of the host</dd>
|
||||
<dt><b>IP</b></dt>
|
||||
<dd>IP-Adress of the host (or broadcast address of the local network if IP of the host is unknown)</dd>
|
||||
<dt><b>mode <i>[EW|UDP]</i></b></dt>
|
||||
<dd>EW: wakeup by <i>usr/bin/ether-wake</i> </dd>
|
||||
<dd>UDP: wakeup by an implementation like <i>Net::Wake(CPAN)</i></dd>
|
||||
</dl>
|
||||
<br><br>
|
||||
|
||||
Example:
|
||||
<b><font size="+1">Examples</font></b>:
|
||||
<ul>
|
||||
<code>define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24</code><br>
|
||||
<code>define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 switching only one time</code><br>
|
||||
<code>define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 EW by ether-wake(linux command)</code><br>
|
||||
<code>define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 BOTH by both methods</code><br>
|
||||
<code>define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 UDP 200 in repeat mode<i><b>usr/bin/ether-wake</b></i> in repeatmode</code><br>
|
||||
</ul>
|
||||
Notes:
|
||||
<br><br>
|
||||
|
||||
<b><font size="+1">Notes</font></b>:
|
||||
<ul>
|
||||
<li>Module uses <code>ether-wake</code> on FritzBoxes.</li>
|
||||
<li>For other computers the WOL implementation of <a href="http://search.cpan.org/~clintdw/Net-Wake-0.02/lib/Net/Wake.pm">Net::Wake</a> is used</li>
|
||||
Not every hardware is able to wake up other devices by default. Oftenly firewalls filter magic packets. Switch them first off.
|
||||
You may need a packet sniffer to check some malfunktion.
|
||||
With this module you get two methods to do the job: see the mode parameter.
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<a name="WOLset"></a>
|
||||
<h4>Set </h4>
|
||||
<ul>
|
||||
<code>set <name> <value></code>
|
||||
<code><b><font size="+1">set <name> <value></font></b></code>
|
||||
<br><br>
|
||||
where <code>value</code> is one of:<br>
|
||||
<pre>
|
||||
refresh # checks whether the computer is currently running
|
||||
on # sends a magic packet to the defined MAC address
|
||||
<b>refresh</b> # checks(by ping) whether the device is currently running
|
||||
<b>on</b> # sends a magic packet to the defined MAC address
|
||||
<b>off</b> # stops sending magic packets and sends the <b>shutdownCmd</b>(see attributes)
|
||||
</pre>
|
||||
|
||||
Examples:
|
||||
<b><font size="+1">Examples</font></b>:
|
||||
<ul>
|
||||
<code>set computer1 on</code><br>
|
||||
<code>set computer1 off</code><br>
|
||||
<code>set computer1 refresh</code><br>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<a name="WOLattr"></a>
|
||||
<h4>Attributes</h4>
|
||||
<h4>Attributes</h4>
|
||||
<ul>
|
||||
<li><a name="owtherm_stateAL"><code>attr <name> shutdownCmd <string></code></a>
|
||||
<br />Custom command executed to shutdown a remote machine, i.e. <code>sh /path/to/some/shell/script.sh</code></li>
|
||||
<li><code>attr <name> shutdownCmd <string></code>
|
||||
<br>Custom command executed to shutdown a remote machine, i.e. <code>sh /path/to/some/shell/script.sh</code></li>
|
||||
<li><code>attr <name> interval <seconds></code></a>
|
||||
<br>defines the time between two checks by a <i>ping</i> if state of <name> is <i>on</i></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
||||
=cut
|
Loading…
x
Reference in New Issue
Block a user