# $Id$
##############################################################################
#
# 83_IOhomecontrol.pm
# Copyright by Dr. Boris Neubert
# e-mail: omega at online dot de
#
# 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 HttpUtils;
use JSON;
use Data::Dumper;
#####################################
sub IOhomecontrol_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "IOhomecontrol_Define";
$hash->{UndefFn} = "IOhomecontrol_Undef";
$hash->{GetFn} = "IOhomecontrol_Get";
$hash->{SetFn} = "IOhomecontrol_Set";
$hash->{parseParams} = 1;
#$hash->{AttrFn} = "IOhomecontrol_Attr";
$hash->{AttrList} = "setCmds " . $readingFnAttributes;
}
#####################################
sub IOhomecontrol_Define($$) {
# define IOhomecontrol
my ($hash, $argref, undef) = @_;
my @def= @{$argref};
if($#def != 4) {
my $msg = "wrong syntax: define IOhomecontrol ";
Log 2, $msg;
return $msg;
}
my $name = $def[0];
my $unused = $def[2];
my $host = $def[3];
my $pwfile = $def[4];
$hash->{"host"}= $host;
$hash->{"pwfile"}= $pwfile;
$hash->{fhem}{".token"}= undef;
$hash->{fhem}{".scenes"}= undef;
$hash->{STATE} = "Initialized";
return undef;
}
#####################################
sub IOhomecontrol_Undef($$) {
return undef;
}
#####################################
# Internals
#####################################
sub IOhomecontrol_Exec($$$$) {
my ($hash, $api, $action, $params) = @_;
my $name = $hash->{NAME};
my $host = $hash->{"host"};
my $token = $hash->{fhem}{".token"};
# build header
my $header = {
"Accept" => "application/json",
"Content-Type" => "application/json;charset=utf-8",
};
if(defined($token)) {
$header->{"Authorization"} = "Bearer $token";
};
# build payload
my $payload = {
"action" => $action,
"params" => $params,
};
my $json = encode_json $payload;
#Debug "IOhomecontrol $name: sending $json";
# build HTTP request
my $httpParams = {
url => "http://$host/api/v1/$api",
timeout => 2,
method => "POST",
noshutdown => 1,
keepalive => 0,
httpversion => "1.1",
header => $header,
data => $json,
};
my ($err, $data)= HttpUtils_BlockingGet($httpParams);
if(defined($err) && $err) {
Log3 $hash, 2, "IOhomecontrol $name returned error: $err";
return undef;
} else {
if(defined($data) && $data) {
# strip junk from the beginning
$data =~ s/^\)\]\}\',//;
#Debug "IOhomecontrol $name: data $data";
my $result = decode_json $data;
#Debug Dumper $result;
my $errorsref= $result->{errors};
my @errors= @{$errorsref};
$err= "";
if(@errors) {
$err= join(" ", @errors);
Log3 $hash, 2, "IOhomecontrol $name: API $api, action $action returned errors ($err).";
};
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "deviceStatus", $result->{deviceStatus});
readingsBulkUpdate($hash, "errors", $err);
readingsEndUpdate($hash, 1);
return undef if(@errors);
return $result; # this is a hash reference
} else {
Log3 $hash, 2, "IOhomecontrol $name returned no data.";
return undef;
}
}
}
#####################################
sub IOhomecontrol_getPassword($) {
my $hash = shift;
my $pwfile= $hash->{"pwfile"};
if(open(PWFILE, $pwfile)) {
my @contents= ;
close(PWFILE);
return undef unless @contents;
my $password = $contents[0];
chomp $password;
return $password;
} else {
return undef;
}
}
sub IOhomecontrol_Login($) {
my $hash = shift;
my $name = $hash->{NAME};
Log3 $hash, 4, "IOhomecontrol $name: Logging in...";
my $password = IOhomecontrol_getPassword($hash);
if(!defined($password)) {
Log3 $hash, 2, "IOhomecontrol $name: No password.";
return 0;
}
my $params = { "password" => $password };
my $result= IOhomecontrol_Exec($hash, "auth", "login", $params);
if(defined($result)) {
my $token= $result->{token};
Log3 $hash, 4, "IOhomecontrol $name got token: $token";
$hash->{fhem}{".token"}= $token;
return 1;
}
return 0;
}
sub IOhomecontrol_Logout($) {
my $hash = shift;
my $name = $hash->{NAME};
Log3 $hash, 4, "IOhomecontrol $name: Logging out...";
my $params = { };
my $result= IOhomecontrol_Exec($hash, "auth", "logout", $params);
if(defined($result)) {
Log3 $hash, 4, "IOhomecontrol $name logged out.";
$hash->{fhem}{".token"}= undef;
}
return undef;
}
sub IOhomecontrol_Action($$$$) {
my ($hash, $api, $action, $params) = @_;
my $name = $hash->{NAME};
Log3 $hash, 4, "IOhomecontrol $name: API $api, action $action";
if(IOhomecontrol_Login($hash)) {
my $result= IOhomecontrol_Exec($hash, $api, $action, $params);
IOhomecontrol_Logout($hash);
return $result;
}
return undef;
}
#####################################
sub IOhomecontrol_makeScenes($) {
my $hash= shift;
my $scenes= $hash->{fhem}{".scenes"};
if(!defined($scenes)) {
$scenes= IOhomecontrol_getScenes($hash);
$hash->{fhem}{".scenes"} = $scenes;
}
my $sc= {};
if(defined($scenes)) {
my $data= $scenes->{data};
#Debug "data: " . Dumper $data;
foreach my $item (@{$data}) {
#Debug "data item: " . Dumper $item;
my $name= $item->{name};
my $id= $item->{id};
#Debug "$id: $name";
$sc->{$id}= $name;
}
my $sns= "";
foreach my $id (sort keys %{$sc}) {
$sns.="," if($sns);
$sns.= sprintf("%d: %s", $id, $sc->{$id});
}
readingsSingleUpdate($hash, "scenes", $sns, 1);
}
return $sc; # a hash id => name
}
sub IOhomecontrol_getScenes($) {
my $hash= shift;
my $scenes= IOhomecontrol_Action($hash, "scenes", "get", {});
#Debug Dumper $scenes;
return $scenes;
}
sub IOhomecontrol_runSceneById($$$) {
my ($hash, $id, $name)= @_;
IOhomecontrol_Action($hash, "scenes", "run", { id => $id });
readingsSingleUpdate($hash, "lastScene", $name, 1);
}
#####################################
sub IOhomecontrol_Get($@) {
my ($hash, $argsref, undef) = @_;
my @a= @{$argsref};
return "get needs at least one parameter" if(@a < 2);
my $name = $a[0];
my $cmd= $a[1];
my $arg = ($a[2] ? $a[2] : "");
my @args= @a; shift @args; shift @args;
my $answer= "";
if($cmd eq "scenes") {
$hash->{fhem}{".scenes"}= undef; # forget scenes forces get from device
my $sc= IOhomecontrol_makeScenes($hash);
foreach my $id (sort keys %{$sc}) {
$answer.="\n" if($answer);
$answer.= sprintf("%2d: %s", $id, $sc->{$id});
}
} else {
return "Unknown argument $cmd, choose one of scenes:noArg";
}
return $answer;
}
#####################################
# sub IOhomecontrol_Attr($@) {
#
# my @a = @_;
# my $hash= $defs{$a[1]};
# my $name= $hash->{NAME};
#
# if($a[0] eq "set") {
# if($a[2] eq "") {
# }
# }
#
# return undef;
# }
#####################################
sub IOhomecontrol_getSetCmds($) {
my $hash= shift;
my $name= $hash->{NAME};
my $attr= AttrVal($name, "setCmds", "");
my (undef, $setCmds)= parseParams($attr,",");
return $setCmds;
}
sub IOhomecontrol_Set($$$) {
my ($hash, $argsref, undef) = @_;
my @a= @{$argsref};
return "set needs at least one parameter" if(@a < 2);
my $name = shift @a;
my $cmd= shift @a;
my $setCmds= IOhomecontrol_getSetCmds($hash);
my $usage= "Unknown argument $cmd, choose one of scene " .
join(" ", (keys %{$setCmds}));
if(exists($setCmds->{$cmd})) {
readingsSingleUpdate($hash, "state", $cmd, 1);
my $subst= $setCmds->{$cmd};
Log3 $hash, 4, "IOhomecontrol $name: substitute set command $cmd by $subst";
($argsref, undef)= parseParams($subst);
@a= @{$argsref};
$cmd= shift @a;
}
if($cmd eq "scene") {
if($#a) {
Debug Dumper @a;
return "Command scene needs exactly one argument.";
} else {
my $sc= IOhomecontrol_makeScenes($hash);
my $id= $a[0];
if($id !~ /^\d+$/) {
#Debug "IOhomecontrol $name: looking up scene $id by name...";
my %cs= reverse %{$sc};
$id= $cs{$id};
}
my $sn= $sc->{$id};
if(defined($sn)) {
Log3 $hash, 4, "IOhomecontrol $name: running scene id $id, name $sn";
IOhomecontrol_runSceneById($hash, $id, $sn);
} else {
return "No such scene $id";
}
}
} else {
return $usage
}
return undef;
}
#####################################
1;
=pod
=item device
=item summary control IOhomecontrol devices via REST API
=item summary_DE IOhomecontrol-Geräte mittels REST-API steuern
=begin html
IOhomecontrol
Define
define <name> IOhomecontrol <model> <host> <pwfile>
Defines a IOhomecontrol device. <model>
is a placeholder for future amendments. <host>
is the IP address or hostname of the IOhomecontrol device. <pwfile>
is a file that contains the password to log into the device.
Example:
define velux IOhomecontrol KLF200 192.168.0.91 /opt/fhem/etc/veluxpw.txt
Set
set <name> scene <id>
Runs the scene identified by <id>
which can be either the numeric id of the scene or the scene's name.
Examples:
set velux scene 1
set velux scene "all shutters down"
Scene names with blanks must be enclosed in double quotes.
Get
get <name> scenes
Retrieves the ids and names of the scenes from the device.
Example:
Attributes
- setCmds: a comma-separated list of set command definitions.
Every definition is of the form
<shorthand>=<command>
. This defines a new single-word command <shorthand>
as a substitute for <command>
.
Example: attr velux setCmds up=scene "3.dz.roll2 100%",down=scene "3.dz.roll2 0%"
Substituted commands (and only these) are shown in the state reading. This is useful in conjunction with the devStateIcon
attribute, e.g. attr velux devStateIcon down:shutter_closed up:shutter_open
.
- readingFnAttributes
=end html
=cut