################################################################ # fhem-module for NetIO 230B Power Distribution Unit # # http://www.koukaam.se/showproduct.php?article_id=1502 # # Usage: # There are 2 modes: # # (1) - define the IP, user and password directly in fhem's fhem.cfg (or UI). # # define NetIO1 [ ]; # e.g. define NetIO1 192.168.178.2:80 1 admin admin; # # if you omit the user credentials, the module will look for a configuration file, # if no configuration file is found, it tries with 'admin', 'admin' # # # (2) - define your credentials using a config file. # # define NetIO1 []; # define NetIO1 192.168.178.2:80 1 /var/log/fhem/netio.conf); # # if you omit the configuration parameter, the module will look for a configuration # file at: /var/log/fhem/netio.conf # # NetIO230B Configuration file format: # # %config= ( # host => "192.168.xx.xx:80", # user => "anyusername_without_spaces", # password => "anypassword_without_spaces" # ); # ################################################################ # created 2012 by Andy Fuchs #--------- # Changes: #--------- # 2012-02-03 0.1 initial realease # 2012-02-25 0.2 removed dependencies for LWP::UserAgent and HTTP::Request; # 2012-09-15 0.3 fixed missing param-list; # added slight checking of passed device-address (now explicitely requires setting a port) ################################################################ package main; use strict; use warnings; use Data::Dumper; use IO::Socket; use HttpUtils; use constant PARAM_NAME => 1; use constant PARAM_HOST => 2; use constant PARAM_SOCK => 3; use constant PARAM_USER => 4; use constant PARAM_PASS => 5; use constant PARAM_FILE => 4; use constant DEBUG => 1; sub NetIO230B_Initialize($) { my ($hash) = @_; $hash->{SetFn} = "NetIO230B_Set"; $hash->{GetFn} = "NetIO230B_Get"; $hash->{DefFn} = "NetIO230B_Define"; $hash->{AttrList} = ""; } ################################### sub NetIO230B_Set($@) { my ($hash, @a) = @_; return "no set value specified" if(int(@a) != 2); return "Unknown argument $a[1], choose one of on off " if($a[1] eq "?"); my $state = $a[1]; #initialize state to the passed parameter my $result = "command was not executed - $state is not 'on' or 'off'"; if ($state eq "on" || $state eq "off") { $hash->{STATE} = $state; $state = int($state eq "on"); #prepare the sockets default parameters; 'u' means: don't touch my @values=("u","u","u","u"); my @sockets = @{$hash->{SOCKETS}}; foreach (@sockets) { $values[$_-1] = $state; $hash->{READINGS}{"socket$_"}{TIME} = TimeNow(); $hash->{READINGS}{"socket$_"}{VAL} = $state; } $result = NetIO230B_Request($hash, "set", join("",@values)); } Log3 $hash, 3, "NetIO230B set @a => $result"; return undef; } ################################### sub NetIO230B_Get($@) { my ($hash, @a) = @_; my $result = NetIO230B_Request($hash, "get"); Log3 $hash, 3, "NetIO230B get @a => $result"; return $hash->{STATE}; } ################################### sub NetIO230B_Request($@) { my ($hash, $cmd, $list) = @_; my $URL=''; my $timeout=4; my $noshutdown=1; my $log=''; my $parm='l'; if($cmd eq "set") { $parm = $list; } my $response = GetFileFromURL("http://"."$hash->{HOST}/tgi/control.tgi?l=p:". $hash->{USER}.":".$hash->{PASS}."&p=".$parm, $timeout, undef, $noshutdown ); if(!$response or length($response)==0) { Log3 $hash, 3, "NetIO230B_Request failed: ".$log; return(""); } # strip html tags $response =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gs; # strip leading whitespace $response =~ s/^\s+//; #strip trailing whitespace $response =~ s/\s+$//; return $response if ($cmd eq "set"); #+++todo #555 FORBIDDEN #split the result into an array my @values=split(/ */,$response); #save the values to the readings hash my $state = "???"; my @sockets = @{$hash->{SOCKETS}}; foreach (@sockets) { $hash->{READINGS}{"socket$_"}{TIME} = TimeNow(); $hash->{READINGS}{"socket$_"}{VAL} = $values[$_-1]; if ($state == "???") { #initialize state $state = $values[$_-1]; } else { $state = "???" if ($values[$_-1] != $state); #if states are mixed show ??? } } $hash->{STATE} = $state; # debug output #my %k = %{$hash->{READINGS}}; #foreach my $r (sort keys %k) { # Log3 $hash, 1, "$r S: $k{$r}{VAL} T: $k{$r}{TIME}"; #} return $response; } ### _Define routing is called when fhem starts -> 'reloading' of the module does not call the define routine!! ### sub NetIO230B_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $paramCount = int(@a); Log3 $hash, 3, "Wrong syntax: use 'define NetIO230B : [ ]' or 'define NetIO230B : [ ]'" if(int(@a) < 4); #5 = mit user/pass #4 = mit config #provide some default settings $hash->{CONFIGFILEPATH} = "/var/log/fhem/netio.conf"; #default file path is /var/log/fhem/netio.conf $hash->{USER} = "admin"; $hash->{PASS} = "admin"; @{$hash->{SOCKETS}} = (1,2,3,4); #default to all sockets #mandatory parameter: 'HOST' $hash->{HOST} = $a[PARAM_HOST]; #can be overridden, if using a config file #mandatory parameter: 'SOCKET #'; negative numbers are ignored my $buf = $a[PARAM_SOCK]; if (($buf =~ m/^\d+$/) && ($buf >=0)) { #make sure input value is a positive number if ($buf == 0) #input socket '0' is used as 'all' { @{$hash->{SOCKETS}} = (1,2,3,4); #use this number as 'array of socket-numbers' to operate on } elsif ($buf <= 4) #input socket is a single number <=4 { @{$hash->{SOCKETS}} = ($buf); #use this number as 'array of socket-numbers' to operate on } else { @{$hash->{SOCKETS}} = split("",$buf); #convert the input values to an array of sockets to operate on } } #optional parameter: 'CONFIGFILE_NAME_or_PATH' if ($paramCount == 5) #seems a config file is passed { $hash->{CONFIGFILEPATH} = $a[PARAM_FILE] if defined($a[PARAM_FILE]);; } #optional parameters: 'USER and PASS' if ($paramCount != 6) { my %config = NetIO230B_GetConfiguration($hash); if (%config) { $hash->{HOST} = $config{host} if (defined($config{host})); $hash->{USER} = $config{user} if (defined($config{user})); $hash->{PASS} = $config{password} if (defined($config{password})); } else { Log3 $hash, 3, "NetIO230B: Configuration could not be read. Trying default values...\n"; } } else { #in any other case $hash->{USER} = $a[PARAM_USER] if defined($a[PARAM_USER]); $hash->{PASS} = $a[PARAM_PASS] if defined($a[PARAM_PASS]); } return "NetIO230B: Invalid device-address! Please use an address in the format: :" unless ($hash->{HOST} =~ m/^(.+):([0-9]+)$/); Log3 $hash, 3, "NetIO230B: device opened at host: $hash->{HOST} => @a\n"; return undef; } ########################################## # # NetIO230B Configuration-Format: # # %config= ( # host => "192.168.xx.xx", # user => "anyusername_without_spaces", # password => "anypassword_without_spaces" # ); # # ########################################## ### _GetConfiguration reads a plain text file containing arbitrary information sub NetIO230B_GetConfiguration($) { my ($hash)= @_; my $configfilename = $hash->{CONFIGFILEPATH}; if(!open(CONFIGFILE, $configfilename)) { Log3 $hash, 3, "NetIO230B: Cannot open settings file '$configfilename'."; return (); } my @configfile=; close(CONFIGFILE); my %config; eval join("", @configfile); return %config; } 1; =pod =begin html

NetIO230B

    fhem-module for NetIO 230B Power Distribution Unit    (see: NetIO 230B (koukaam.se))

    Note: this module needs the HTTP::Request and LWP::UserAgent perl modules.
    Please also note: the PDU must use firmware 3.1 or later and set to unencrypted mode.

    Define
    • define <name> NetIO230B <ip-address> <socket number(s) > [<user name> <password>]
    • define <name> NetIO230B <ip-address> <socket number(s) > [<config file path>]
    • Defines a switching device, where sockets can be switched

      • separately (just use 0-4 as socket number)
      • all together (use 1234 as socket number)
      • in arbitrary groups (e.g 13 switches socket 1 and 3, 42 switches socket 2 and 4, etc...), invalid numbers are ignored

      User name and password are optional. When no user name or password is passed, the module looks for a configfile at '/var/log/fhem/netio.conf'. If no config file is found, it uses 'admin/admin' as user/pass, since this is the default configuration for the device.

      Alternatively you can pass a path to a configfile instead of the user/pass combo. (e.g. /var/tmp/tmp.conf) Configfile-Format:

        %config= (
           host => "192.168.61.40",
           user => "admin",
           password => "admin"
        );


        (All settings optional)

      Examples:

      • define Socket3 NetIO230B 192.168.178.10 3
      • define Socket1_and_4 NetIO230B 192.168.178.10 14
      • define coffeemaker NetIO230B 192.168.178.10 1 username secretpassword
      • define coffeemaker_and_light NetIO230B 192.168.178.10 23 /var/log/kitchen.conf

    Get
      get <name> state

      returns the state of the socket(s)
      Example:
        get coffeemaker_and_light   => on or off

    Set
      set <name> <value>

      where value is one of:
      		on
      		off
      		
      Examples:
        set coffeemaker_and_light on

=end html =cut