diff --git a/FHEM/98_RandomTimer.pm b/FHEM/98_RandomTimer.pm index 80a0de781..f3c0f6c6c 100644 --- a/FHEM/98_RandomTimer.pm +++ b/FHEM/98_RandomTimer.pm @@ -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) = @_; diff --git a/FHEM/98_WOL.pm b/FHEM/98_WOL.pm index 7afbe5831..68a8632cc 100644 --- a/FHEM/98_WOL.pm +++ b/FHEM/98_WOL.pm @@ -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 . +# +############################################################################## 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 WOL MAC_ADRESS IP"; + my $u = "wrong syntax: define WOL "; 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

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> - <unitcode> + define <name> WOL <MAC> <IP> [<mode> [<repeat>]]

      - Defines a WOL device via its MAC and IP address.

      +
      +
      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)
      +
      +

      - Example: + Examples:
        - define computer1 WOL 72:11:AC:4D:37:13 192.168.0.24
        + 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: +

      + + Notes:
        -
      • Module uses ether-wake on FritzBoxes.
      • -
      • For other computers the WOL implementation of Net::Wake is used
      • + 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> + set <name> <value>

      where value is one of:
      -    refresh           # checks whether the computer is currently running
      -    on                # sends a magic packet to the defined MAC address
      +    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: + Examples:
        set computer1 on
        + set computer1 off
        set computer1 refresh
    -

    Attributes

    +

    Attributes

      -
    • attr <name> shutdownCmd <string> -
      Custom command executed to shutdown a remote machine, i.e. sh /path/to/some/shell/script.sh
    • +
    • 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 +=cut \ No newline at end of file