| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- # $Id: 83_IOhomecontrol.pm 15083 2017-09-17 16:43:41Z neubert $
- ##############################################################################
- #
- # 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 <http://www.gnu.org/licenses/>.
- #
- ##############################################################################
- 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 <name> IOhomecontrol <model> <host> <pwfile>
- my ($hash, $argref, undef) = @_;
-
- my @def= @{$argref};
- if($#def != 4) {
- my $msg = "wrong syntax: define <name> IOhomecontrol <model> <host> <pwfile>";
- 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= <PWFILE>;
- 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
- <a name="IOhomecontrol"></a>
- <h3>IOhomecontrol</h3>
- <ul>
- <a name="IOhomecontroldefine"></a>
- <b>Define</b><br><br>
- <ul>
- <code>define <name> IOhomecontrol <model> <host> <pwfile> </code><br><br>
- Defines a IOhomecontrol device. <code><model></code> is a placeholder for future amendments. <code><host></code> is the IP address or hostname of the IOhomecontrol device. <code><pwfile></code> is a file that contains the password to log into the device.<br><br>
- Example:
- <ul>
- <code>define velux IOhomecontrol KLF200 192.168.0.91 /opt/fhem/etc/veluxpw.txt</code><br>
- </ul>
- <br><br>
- </ul>
- <a name="IOhomecontrolset"></a>
- <b>Set</b><br><br>
- <ul>
- <code>set <name> scene <id></code>
- <br><br>
- Runs the scene identified by <code><id></code> which can be either the numeric id of the scene or the scene's name.
- <br><br>
- Examples:
- <ul>
- <code>set velux scene 1</code><br>
- <code>set velux scene "all shutters down"</code><br>
- </ul>
- <br>
- Scene names with blanks must be enclosed in double quotes.
- <br><br>
- </ul>
- <a name="IOhomecontrolget"></a>
- <b>Get</b><br><br>
- <ul>
- <code>get <name> scenes</code>
- <br><br>
- Retrieves the ids and names of the scenes from the device.
- <br><br>
- Example:
- <ul>
- <code>get velux scenes</code><br>
- </ul>
- </ul>
- <br><br>
-
-
- <a name="IOhomecontrolattr"></a>
- <b>Attributes</b>
- <ul>
- <li>setCmds: a comma-separated list of set command definitions.
- Every definition is of the form <code><shorthand>=<command></code>. This defines a new single-word command <code><shorthand></code> as a substitute for <code><command></code>.<br>
- Example: <code>attr velux setCmds up=scene "3.dz.roll2 100%",down=scene "3.dz.roll2 0%"</code><br>
- Substituted commands (and only these) are shown in the state reading. This is useful in conjunction with the <code>devStateIcon</code> attribute, e.g. <code>attr velux devStateIcon down:shutter_closed up:shutter_open</code>.</li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- </ul>
- <br><br>
- </ul>
- =end html
- =cut
|