fhem-mirror/FHEM/24_NetIO230B.pm
rudolfkoenig 991f1684f4 all modules/files maintained by me: loglevel removed, Log3 uses the verbose
attribute, which is available from now on for all devices.


git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@3738 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2013-08-18 14:13:59 +00:00

387 lines
10 KiB
Perl

################################################################
# 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 <IP address:port number> <socket number> [<user> <password>];
# 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 <IP address:port number> <socket number> [<path_to_configuration_file>];
# 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 <name> NetIO230B <ip-address>:<portnumber> [<socket_number> <username> <password>]' or 'define <name> NetIO230B <ip-address>:<portnumber> [<socket_number> <configfilename>]'" 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: <ip-address>:<portnumber>" 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=<CONFIGFILE>;
close(CONFIGFILE);
my %config;
eval join("", @configfile);
return %config;
}
1;
=pod
=begin html
<a name="NetIO230B"></a>
<h3>NetIO230B</h3>
<ul>
<p>
fhem-module for NetIO 230B Power Distribution Unit &nbsp;&nbsp; (see: <a
href="http://www.koukaam.se/showproduct.php?article_id=1502">NetIO 230B
(koukaam.se)</a>)
</p>
Note: this module needs the HTTP::Request and LWP::UserAgent perl modules.
<br />
Please also note: the PDU must use firmware 3.1 or later and set to unencrypted mode.
<br /><br />
<a name="NETIO230Bdefine"></a>
<b>Define</b>
<ul>
<li><code>define &lt;name&gt; NetIO230B &lt;ip-address&gt; &lt;socket number(s)
&gt; [&lt;user name&gt; &lt;password&gt;]</code></li>
<li><code>define &lt;name&gt; NetIO230B &lt;ip-address&gt; &lt;socket number(s)
&gt; [&lt;config file path&gt;]</code></li>
<p>
Defines a switching device, where sockets can be switched
</p>
<ul>
<li>separately (just use 0-4 as socket number)</li>
<li>all together (use 1234 as socket number)</li>
<li>in arbitrary groups (e.g 13 switches socket 1 and 3, 42
switches socket 2 and 4, etc...), invalid numbers are
ignored</li>
</ul>
<p>
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.
<p>
Alternatively you can pass a path to a configfile instead of
the user/pass combo. (e.g. /var/tmp/tmp.conf)
Configfile-Format:<br />
<ul>
<code>
%config= (<br />
&nbsp;&nbsp;&nbsp;host => "192.168.61.40",<br />
&nbsp;&nbsp;&nbsp;user => "admin",<br />
&nbsp;&nbsp;&nbsp;password => "admin"<br />
);</code>
<br /><br /><small>(All settings optional)</small>
</ul>
</p>
<p>Examples:</p>
<ul>
<li><code>define Socket3 NetIO230B 192.168.178.10 3</code></li>
<li><code>define Socket1_and_4 NetIO230B 192.168.178.10 14</code></li>
<li><code>define coffeemaker NetIO230B 192.168.178.10 1 username secretpassword</code></li>
<li><code>define coffeemaker_and_light NetIO230B 192.168.178.10 23 /var/log/kitchen.conf</code></li>
</ul>
</ul>
<br>
<a name="NETIO230Bget"></a>
<b>Get </b>
<ul>
<code>get &lt;name&gt; state</code>
<br><br>
returns the state of the socket(s)<br>
Example:
<ul>
<code>get coffeemaker_and_light</code>&nbsp;&nbsp; => <code>on or off</code><br>
</ul>
<br>
</ul>
<a name="NETIO230Bset"></a>
<b>Set </b>
<ul>
<code>set &lt;name&gt; &lt;value&gt;</code>
<br><br>
where <code>value</code> is one of:<br>
<pre>
on
off
</pre>
Examples:
<ul>
<code>set coffeemaker_and_light on</code><br>
</ul>
<br>
</ul>
</ul>
=end html
=cut