# $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 . # ############################################################################## package main; use strict; use warnings; use IO::Socket; use Time::HiRes qw(gettimeofday); ################################################################################ sub WOL_Initialize($) { my ($hash) = @_; $hash->{SetFn} = "WOL_Set"; $hash->{DefFn} = "WOL_Define"; $hash->{UndefFn} = "WOL_Undef"; $hash->{AttrList} = "interval shutdownCmd sysCmd ". $readingFnAttributes; } ################################################################################ sub WOL_Set($@) { my ($hash, @a) = @_; return "no set value specified" if(int(@a) < 2); return "Unknown argument $a[1], choose one of on off refresh" if($a[1] eq "?"); my $name = shift @a; my $v = join(" ", @a); Log3 $hash, 3, "WOL set $name $v"; if ($v eq "on") { $hash->{STATE} = $v; Log3 $hash, 3, "WOL waking $name with MAC $hash->{MAC} IP $hash->{IP} "; } elsif ($v eq "off") { $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") { WOL_GetUpdate($hash); } return undef; } ################################################################################ sub WOL_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $u = "wrong syntax: define WOL "; return $u if(int(@a) < 4); 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) = @_; 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) = @_; if ($hash->{STATE} eq "on") { wake($hash); } if ($hash->{REPEAT} > 0 && $hash->{STATE} eq "on" ) { InternalTimer(gettimeofday()+$hash->{REPEAT}, "WOL_GetUpdate", $hash, 0); } } ################################################################################ sub wake($){ my ($hash) = @_; my $name = $hash->{NAME}; my $mac = $hash->{MAC}; my $host = $hash->{IP}; readingsBeginUpdate ($hash); Log3 $hash, 3, "WOL keeping $name with MAC $mac IP $host busy"; if ($hash->{MODE} eq "BOTH" || $hash->{MODE} eq "EW" ) { wol_by_ew ($hash, $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 wakevia lan, taken from Net::Wake package sub wol_by_udp { 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 : $!"; 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); 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 wake via system command sub wol_by_ew($$) { my ($hash, $mac) = @_; my $sysCmd = AttrVal($hash->{NAME}, "sysCmd", "/usr/bin/ether-wake"); if (-e $sysCmd) { my $response = `$sysCmd $mac`; } else { Log3 $hash, 1, "[$hash->{NAME}] system command '$sysCmd' not found"; } return 1; } 1; =pod =begin html

WOL

Defines a WOL device via its MAC and IP address.

when sending the on 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.

    Define

      define <name> WOL <MAC> <IP> [<mode> [<repeat>]]

      MAC
      MAC-Adress of the host
      IP
      IP-Adress of the host (or broadcast address of the local network if IP of the host is unknown)
      mode [EW|UDP]
      EW: wakeup by usr/bin/ether-wake
      UDP: wakeup by an implementation like Net::Wake(CPAN)


      Examples:
        define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24             switching only one time
        define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 EW          by ether-wake(linux command)
        define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 BOTH        by both methods
        define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24 UDP 200     in repeat modeusr/bin/ether-wake in repeatmode


      Notes:
        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.

    Set

      set <name> <value>

      where value is one of:
          refresh           # checks(by ping) whether the device is currently running
          on                # sends a magic packet to the defined MAC address
          off               # stops sending magic packets and sends the shutdownCmd(see attributes)
          
      Examples:
        set computer1 on
        set computer1 off
        set computer1 refresh

    Attributes

    • attr <name> sysCmd <string>
      Custom command executed to wakeup a remote machine, i.e. /usr/bin/ether-wake or /usr/bin/wakeonlan
    • attr <name> shutdownCmd <string>
      Custom command executed to shutdown a remote machine, i.e. sh /path/to/some/shell/script.sh
    • attr <name> interval <seconds>
      defines the time between two checks by a ping if state of <name> is on
=end html =cut