first commit
This commit is contained in:
commit
313ad5fcb4
586
73_GardenaSmartBridge.pm
Normal file
586
73_GardenaSmartBridge.pm
Normal file
@ -0,0 +1,586 @@
|
|||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Developed with Kate
|
||||||
|
#
|
||||||
|
# (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
|
||||||
|
# All rights reserved
|
||||||
|
#
|
||||||
|
# This script 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
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# The GNU General Public License can be found at
|
||||||
|
# http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||||
|
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||||
|
#
|
||||||
|
# This script 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.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Das JSON Modul immer in einem eval aufrufen
|
||||||
|
# $data = eval{decode_json($data)};
|
||||||
|
#
|
||||||
|
# if($@){
|
||||||
|
# Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@");
|
||||||
|
#
|
||||||
|
# readingsSingleUpdate($hash, "state", "error", 1);
|
||||||
|
#
|
||||||
|
# return;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
###### Wichtige Notizen
|
||||||
|
#
|
||||||
|
# apt-get install libio-socket-ssl-perl
|
||||||
|
# http://www.dxsdata.com/de/2016/07/php-class-for-gardena-smart-system-api/
|
||||||
|
#
|
||||||
|
##
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
|
||||||
|
my $missingModul = "";
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use HttpUtils;
|
||||||
|
use Data::Dumper; #debugging
|
||||||
|
|
||||||
|
eval "use Encode qw(encode encode_utf8 decode_utf8);1" or $missingModul .= "Encode ";
|
||||||
|
eval "use JSON;1" or $missingModul .= "JSON ";
|
||||||
|
###todo Hier fehlt noch Modulabfrage für ssl
|
||||||
|
|
||||||
|
|
||||||
|
my $version = "0.0.10";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Declare functions
|
||||||
|
sub GardenaSmartBridge_Attr(@);
|
||||||
|
sub GardenaSmartBridge_Define($$);
|
||||||
|
sub GardenaSmartBridge_Initialize($);
|
||||||
|
sub GardenaSmartBridge_Set($@);
|
||||||
|
sub GardenaSmartBridge_Write($@);
|
||||||
|
sub GardenaSmartBridge_Undef($$);
|
||||||
|
sub GardenaSmartBridge_ResponseProcessing($$);
|
||||||
|
sub GardenaSmartBridge_ErrorHandling($$$);
|
||||||
|
sub GardenaSmartBridge_encrypt($);
|
||||||
|
sub GardenaSmartBridge_decrypt($);
|
||||||
|
sub GardenaSmartBridge_WriteReadings($$);
|
||||||
|
sub GardenaSmartBridge_ParseJSON($$);
|
||||||
|
sub GardenaSmartBridge_getDevices($);
|
||||||
|
sub GardenaSmartBridge_getToken($);
|
||||||
|
sub GardenaSmartBridge_InternalTimerGetDeviceData($);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Initialize($) {
|
||||||
|
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
|
||||||
|
# Provider
|
||||||
|
$hash->{WriteFn} = "GardenaSmartBridge_Write";
|
||||||
|
$hash->{Clients} = ":GardenaSmartDevice:";
|
||||||
|
$hash->{MatchList} = { "1:GardenaSmartDevice" => '^{"id":".*' };
|
||||||
|
|
||||||
|
|
||||||
|
# Consumer
|
||||||
|
$hash->{SetFn} = "GardenaSmartBridge_Set";
|
||||||
|
$hash->{DefFn} = "GardenaSmartBridge_Define";
|
||||||
|
$hash->{UndefFn} = "GardenaSmartBridge_Undef";
|
||||||
|
|
||||||
|
$hash->{AttrFn} = "GardenaSmartBridge_Attr";
|
||||||
|
$hash->{AttrList} = "debugJSON:0,1 ".
|
||||||
|
"disable:1 ".
|
||||||
|
$readingFnAttributes;
|
||||||
|
|
||||||
|
foreach my $d(sort keys %{$modules{GardenaSmartBridge}{defptr}}) {
|
||||||
|
|
||||||
|
my $hash = $modules{GardenaSmartBridge}{defptr}{$d};
|
||||||
|
$hash->{VERSION} = $version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Define($$) {
|
||||||
|
|
||||||
|
my ( $hash, $def ) = @_;
|
||||||
|
|
||||||
|
my @a = split( "[ \t][ \t]*", $def );
|
||||||
|
|
||||||
|
|
||||||
|
return "too few parameters: define <NAME> GardenaSmartBridge <Email> <Passwort>" if( @a != 4 ) ;
|
||||||
|
return "Cannot define Gardena Bridge device. Perl modul $missingModul is missing." if ( $missingModul );
|
||||||
|
|
||||||
|
my $name = $a[0];
|
||||||
|
my $user = $a[2];
|
||||||
|
my $pass = $a[3];
|
||||||
|
$hash->{BRIDGE} = 1;
|
||||||
|
$hash->{URL} = 'https://sg-api.dss.husqvarnagroup.net/sg-1';
|
||||||
|
$hash->{VERSION} = $version;
|
||||||
|
$hash->{INTERVAL} = 300;
|
||||||
|
|
||||||
|
my $username = GardenaSmartBridge_encrypt($user);
|
||||||
|
my $password = GardenaSmartBridge_encrypt($pass);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - encrypt $user/$pass to $username/$password" if($user ne $username || $pass ne $password);
|
||||||
|
$hash->{DEF} = "$username $password";
|
||||||
|
|
||||||
|
$hash->{helper}{username} = $username;
|
||||||
|
$hash->{helper}{password} = $password;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$attr{$name}{room} = "GardenaSmart" if( !defined( $attr{$name}{room} ) );
|
||||||
|
|
||||||
|
readingsSingleUpdate($hash,'state','initialized',1);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - defined GardenaSmartBridge and crypt your credentials";
|
||||||
|
|
||||||
|
|
||||||
|
if( $init_done ) {
|
||||||
|
|
||||||
|
GardenaSmartBridge_getToken($hash);
|
||||||
|
readingsSingleUpdate($hash,'state','get token',1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
InternalTimer( gettimeofday()+15, "GardenaSmartBridge_getToken", $hash, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$modules{GardenaSmartBridge}{defptr}{BRIDGE} = $hash;
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Undef($$) {
|
||||||
|
|
||||||
|
my ( $hash, $arg ) = @_;
|
||||||
|
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
delete $modules{GardenaSmartBridge}{defptr}{BRIDGE} if( defined($modules{GardenaSmartBridge}{defptr}{BRIDGE}) );
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Attr(@) {
|
||||||
|
|
||||||
|
my ( $cmd, $name, $attrName, $attrVal ) = @_;
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
|
||||||
|
if( $attrName eq "disable" ) {
|
||||||
|
if( $cmd eq "set" and $attrVal eq "1" ) {
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
readingsSingleUpdate ( $hash, "state", "inactive", 1 );
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif( $cmd eq "del" ) {
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
GardenaSmartBridge_InternalTimerGetDeviceData($hash);
|
||||||
|
readingsSingleUpdate ( $hash, "state", "active", 1 );
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - enabled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif( $attrName eq "disabledForIntervals" ) {
|
||||||
|
if( $cmd eq "set" ) {
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - disabledForIntervals";
|
||||||
|
readingsSingleUpdate ( $hash, "state", "inactive", 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif( $cmd eq "del" ) {
|
||||||
|
readingsSingleUpdate ( $hash, "state", "active", 1 );
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - enabled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif( $attrName eq "interval" ) {
|
||||||
|
if( $cmd eq "set" ) {
|
||||||
|
$hash->{INTERVAL} = $attrVal;
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - set interval: $attrVal";
|
||||||
|
GardenaSmartBridge_InternalTimerGetDeviceData($hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
elsif( $cmd eq "del" ) {
|
||||||
|
$hash->{INTERVAL} = 300;
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - delete User interval and set default: 300";
|
||||||
|
GardenaSmartBridge_InternalTimerGetDeviceData($hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Set($@) {
|
||||||
|
|
||||||
|
my ($hash, $name, $cmd, @args) = @_;
|
||||||
|
my ($arg, @params) = @args;
|
||||||
|
|
||||||
|
|
||||||
|
if( lc $cmd eq 'getdevicesstate' ) {
|
||||||
|
|
||||||
|
GardenaSmartBridge_getDevices($hash);
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq 'dummy2' ) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
my $list = "getDevicesState:noArg";
|
||||||
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_InternalTimerGetDeviceData($) {
|
||||||
|
|
||||||
|
my $hash = shift;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
if( not IsDisabled($name) ) {
|
||||||
|
|
||||||
|
GardenaSmartBridge_getDevices($hash);
|
||||||
|
Log3 $name, 4, "GardenaSmartBridge ($name) - set internal timer function for recall InternalTimerGetDeviceData sub";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
readingsSingleUpdate($hash,'state','disabled',1);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - device is disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalTimer( gettimeofday()+$hash->{INTERVAL},"GardenaSmartBridge_InternalTimerGetDeviceData", $hash, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_Write($@) {
|
||||||
|
|
||||||
|
my ($hash,$payload,$deviceId,$model) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
my $session_id = $hash->{helper}{session_id};
|
||||||
|
my $header = "Content-Type: application/json";
|
||||||
|
my $uri = '';
|
||||||
|
my $method = 'POST';
|
||||||
|
$header .= "\r\nX-Session: $session_id" if( defined($hash->{helper}{session_id}) );
|
||||||
|
$payload = '{' . $payload . '}' if( defined($payload) );
|
||||||
|
$payload = '{}' if( not defined($payload) );
|
||||||
|
|
||||||
|
|
||||||
|
if( $payload eq '{}' ) {
|
||||||
|
$method = 'GET';
|
||||||
|
$uri .= '/locations/?user_id=' . $hash->{helper}{user_id} if( not defined($hash->{helper}{locations_id}) );
|
||||||
|
readingsSingleUpdate($hash,'state','fetch locationId',1) if( not defined($hash->{helper}{locations_id}) );
|
||||||
|
$uri .= '/sessions' if( not defined($hash->{helper}{session_id}));
|
||||||
|
$uri .= '/devices' if( not defined($model) and defined($hash->{helper}{locations_id}) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri .= '/sessions' if( not defined($hash->{helper}{session_id}));
|
||||||
|
$uri .= '/devices/' . $deviceId . '/abilities/' . $model . '/command' if( defined($model) and defined($payload) );
|
||||||
|
$uri .= '?locationId=' . $hash->{helper}{locations_id} if( defined($hash->{helper}{locations_id}) );
|
||||||
|
|
||||||
|
|
||||||
|
HttpUtils_NonblockingGet(
|
||||||
|
{
|
||||||
|
url => $hash->{URL} . $uri,
|
||||||
|
timeout => 15,
|
||||||
|
hash => $hash,
|
||||||
|
device_id => $deviceId,
|
||||||
|
data => $payload,
|
||||||
|
method => $method,
|
||||||
|
header => $header,
|
||||||
|
doTrigger => 1,
|
||||||
|
callback => \&GardenaSmartBridge_ErrorHandling
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Log3 $name, 4, "GardenaSmartBridge ($name) - Send with URL: $hash->{URL}$uri, HEADER: $header, DATA: $payload, METHOD: $method";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_ErrorHandling($$$) {
|
||||||
|
|
||||||
|
my ($param,$err,$data) = @_;
|
||||||
|
|
||||||
|
my $hash = $param->{hash};
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
###todo Das gesamte Errorhandling muss hier noch rein
|
||||||
|
|
||||||
|
#Log3 $name, 1, "GardenaSmartBridge ($name) - Header:\n".Dumper($param->{header});
|
||||||
|
#Log3 $name, 1, "GardenaSmartBridge ($name) - Error:\n".Dumper($err);
|
||||||
|
#Log3 $name, 1, "GardenaSmartBridge ($name) - Data:\n".Dumper($data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
readingsSingleUpdate($hash,'state','connect to cloud',1) if( defined($hash->{helper}{locations_id}) );
|
||||||
|
GardenaSmartBridge_ResponseProcessing($hash,$data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_ResponseProcessing($$) {
|
||||||
|
|
||||||
|
my ($hash,$json) = @_;
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
my $decode_json = eval{decode_json($json)};
|
||||||
|
if($@){
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - JSON error while request: $@";
|
||||||
|
|
||||||
|
if( AttrVal( $name, 'debugJSON', 0 ) == 1 ) {
|
||||||
|
readingsBeginUpdate($hash);
|
||||||
|
readingsBulkUpdate($hash, 'JSON_ERROR', $@, 1);
|
||||||
|
readingsBulkUpdate($hash, 'JSON_ERROR_STRING', $json, 1);
|
||||||
|
readingsEndUpdate($hash, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if( defined($decode_json->{sessions}) and $decode_json->{sessions}) {
|
||||||
|
|
||||||
|
$hash->{helper}{session_id} = $decode_json->{sessions}{token};
|
||||||
|
$hash->{helper}{user_id} = $decode_json->{sessions}{user_id};
|
||||||
|
|
||||||
|
GardenaSmartBridge_Write($hash,undef,undef,undef);
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - fetch locations id";
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} elsif( not defined($hash->{helper}{locations_id}) and defined($decode_json->{locations}) and ref($decode_json->{locations}) eq "ARRAY" and scalar(@{$decode_json->{locations}}) > 0) {
|
||||||
|
|
||||||
|
foreach my $location ( @{$decode_json->{locations}} ) {
|
||||||
|
|
||||||
|
$hash->{helper}{locations_id} = $location->{id};
|
||||||
|
|
||||||
|
GardenaSmartBridge_WriteReadings($hash,$location);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - processed locations id. ID ist " . $hash->{helper}{locations_id};
|
||||||
|
GardenaSmartBridge_Write($hash,undef,undef,undef);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
} elsif( defined($decode_json->{devices}) and ref($decode_json->{devices}) eq "ARRAY" and scalar(@{$decode_json->{devices}}) > 0) {
|
||||||
|
|
||||||
|
my @buffer = split('"devices":\[',$json);
|
||||||
|
|
||||||
|
|
||||||
|
my ($json,$tail) = GardenaSmartBridge_ParseJSON($hash, $buffer[1]);
|
||||||
|
|
||||||
|
|
||||||
|
while($json) {
|
||||||
|
|
||||||
|
Log3 $name, 5, "GardenaSmartBridge ($name) - Decoding JSON message. Length: " . length($json) . " Content: " . $json;
|
||||||
|
Log3 $name, 5, "GardenaSmartBridge ($name) - Vor Sub: Laenge JSON: " . length($json) . " Content: " . $json . " Tail: " . $tail;
|
||||||
|
|
||||||
|
|
||||||
|
unless( not defined($tail) and not ($tail) ) {
|
||||||
|
|
||||||
|
$decode_json = eval{decode_json($json)};
|
||||||
|
if($@){
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - JSON error while request: $@";
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatch($hash,$json,undef)
|
||||||
|
#Log3 $name, 3, "GardenaSmartBridge ($name) - NAME: $decode_json->{name} und ID: $decode_json->{id} und JSON: $json"
|
||||||
|
unless( $decode_json->{category} eq 'gateway' );
|
||||||
|
}
|
||||||
|
|
||||||
|
($json,$tail) = GardenaSmartBridge_ParseJSON($hash, $tail);
|
||||||
|
|
||||||
|
Log3 $name, 5, "GardenaSmartBridge ($name) - Nach Sub: Laenge JSON: " . length($json) . " Content: " . $json . " Tail: " . $tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - no Match for processing data"
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_WriteReadings($$) {
|
||||||
|
|
||||||
|
my ($hash,$decode_json) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
if( defined($decode_json->{id}) and $decode_json->{id} and defined($decode_json->{name}) and $decode_json->{name} ) {
|
||||||
|
|
||||||
|
readingsBeginUpdate($hash);
|
||||||
|
readingsBulkUpdateIfChanged($hash,'name',$decode_json->{name});
|
||||||
|
readingsBulkUpdateIfChanged($hash,'authorized_user_ids',scalar(@{$decode_json->{authorized_user_ids}}));
|
||||||
|
readingsBulkUpdateIfChanged($hash,'devices',scalar(@{$decode_json->{devices}}));
|
||||||
|
|
||||||
|
while( ( my ($t,$v) ) = each %{$decode_json->{geo_position}} ) {
|
||||||
|
$v = encode_utf8($v);
|
||||||
|
readingsBulkUpdateIfChanged($hash,$t,$v);
|
||||||
|
}
|
||||||
|
|
||||||
|
readingsBulkUpdateIfChanged($hash,'zones',scalar(@{$decode_json->{zones}}));
|
||||||
|
readingsEndUpdate( $hash, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - readings would be written";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
####################################
|
||||||
|
####################################
|
||||||
|
#### my little helpers Sub's #######
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_getDevices($) {
|
||||||
|
|
||||||
|
my $hash = shift;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
GardenaSmartBridge_Write($hash,undef,undef,undef);
|
||||||
|
Log3 $name, 4, "GardenaSmartBridge ($name) - fetch device list and device states";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_getToken($) {
|
||||||
|
|
||||||
|
my $hash = shift;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
delete $hash->{helper}{session_id} if( defined($hash->{helper}{session_id}) and $hash->{helper}{session_id} );
|
||||||
|
delete $hash->{helper}{user_id} if( defined($hash->{helper}{user_id}) and $hash->{helper}{user_id} );
|
||||||
|
delete $hash->{helper}{locations_id} if( defined($hash->{helper}{locations_id}) and $hash->{helper}{locations_id} );
|
||||||
|
|
||||||
|
GardenaSmartBridge_Write($hash,'"sessions": {"email": "'.GardenaSmartBridge_decrypt($hash->{helper}{username}).'","password": "'.GardenaSmartBridge_decrypt($hash->{helper}{password}).'"}',undef,undef);
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - send credentials to fetch Token and locationId";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_encrypt($) {
|
||||||
|
|
||||||
|
my ($decoded) = @_;
|
||||||
|
my $key = getUniqueId();
|
||||||
|
my $encoded;
|
||||||
|
|
||||||
|
return $decoded if( $decoded =~ /crypt:/ );
|
||||||
|
|
||||||
|
for my $char (split //, $decoded) {
|
||||||
|
my $encode = chop($key);
|
||||||
|
$encoded .= sprintf("%.2x",ord($char)^ord($encode));
|
||||||
|
$key = $encode.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'crypt:'.$encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_decrypt($) {
|
||||||
|
|
||||||
|
my ($encoded) = @_;
|
||||||
|
my $key = getUniqueId();
|
||||||
|
my $decoded;
|
||||||
|
|
||||||
|
return $encoded if( $encoded !~ /crypt:/ );
|
||||||
|
|
||||||
|
$encoded = $1 if( $encoded =~ /crypt:(.*)/ );
|
||||||
|
|
||||||
|
for my $char (map { pack('C', hex($_)) } ($encoded =~ /(..)/g)) {
|
||||||
|
my $decode = chop($key);
|
||||||
|
$decoded .= chr(ord($char)^ord($decode));
|
||||||
|
$key = $decode.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartBridge_ParseJSON($$) {
|
||||||
|
|
||||||
|
my ($hash, $buffer) = @_;
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $open = 0;
|
||||||
|
my $close = 0;
|
||||||
|
my $msg = '';
|
||||||
|
my $tail = '';
|
||||||
|
|
||||||
|
|
||||||
|
if($buffer) {
|
||||||
|
foreach my $c (split //, $buffer) {
|
||||||
|
if($open == $close && $open > 0) {
|
||||||
|
$tail .= $c;
|
||||||
|
Log3 $name, 5, "GardenaSmartBridge ($name) - $open == $close && $open > 0";
|
||||||
|
|
||||||
|
} elsif(($open == $close) && ($c ne '{')) {
|
||||||
|
|
||||||
|
Log3 $name, 5, "GardenaSmartBridge ($name) - Garbage character before message: " . $c;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if($c eq '{') {
|
||||||
|
|
||||||
|
$open++;
|
||||||
|
|
||||||
|
} elsif($c eq '}') {
|
||||||
|
|
||||||
|
$close++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$msg .= $c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($open != $close) {
|
||||||
|
|
||||||
|
$tail = $msg;
|
||||||
|
$msg = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 4, "GardenaSmartBridge ($name) - return msg: $msg and tail: $tail";
|
||||||
|
return ($msg,$tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=item device
|
||||||
|
=item summary Gardena Smart
|
||||||
|
=item summary_DE Gardena Smart
|
||||||
|
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=end html
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
=cut
|
346
74_GardenaSmartDevice.pm
Normal file
346
74_GardenaSmartDevice.pm
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Developed with Kate
|
||||||
|
#
|
||||||
|
# (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
|
||||||
|
# All rights reserved
|
||||||
|
#
|
||||||
|
# This script 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
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# The GNU General Public License can be found at
|
||||||
|
# http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
# A copy is found in the textfile GPL.txt and important notices to the license
|
||||||
|
# from the author is found in LICENSE.txt distributed with these scripts.
|
||||||
|
#
|
||||||
|
# This script 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.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## Das JSON Modul immer in einem eval aufrufen
|
||||||
|
# $data = eval{decode_json($data)};
|
||||||
|
#
|
||||||
|
# if($@){
|
||||||
|
# Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@");
|
||||||
|
#
|
||||||
|
# readingsSingleUpdate($hash, "state", "error", 1);
|
||||||
|
#
|
||||||
|
# return;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
###### Wichtige Notizen
|
||||||
|
#
|
||||||
|
# apt-get install libio-socket-ssl-perl
|
||||||
|
# http://www.dxsdata.com/de/2016/07/php-class-for-gardena-smart-system-api/
|
||||||
|
#
|
||||||
|
##
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
|
||||||
|
my $missingModul = "";
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Data::Dumper; #debugging
|
||||||
|
|
||||||
|
eval "use Encode qw(encode encode_utf8 decode_utf8);1" or $missingModul .= "Encode ";
|
||||||
|
eval "use JSON;1" or $missingModul .= "JSON ";
|
||||||
|
|
||||||
|
|
||||||
|
my $version = "0.0.10";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Declare functions
|
||||||
|
sub GardenaSmartDevice_Attr(@);
|
||||||
|
sub GardenaSmartDevice_Define($$);
|
||||||
|
sub GardenaSmartDevice_Initialize($);
|
||||||
|
sub GardenaSmartDevice_Set($@);
|
||||||
|
sub GardenaSmartDevice_Undef($$);
|
||||||
|
sub GardenaSmartDevice_WriteReadings($$);
|
||||||
|
sub GardenaSmartDevice_Parse($$);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Initialize($) {
|
||||||
|
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
$hash->{Match} = '^{"id":".*';
|
||||||
|
|
||||||
|
$hash->{SetFn} = "GardenaSmartDevice_Set";
|
||||||
|
$hash->{DefFn} = "GardenaSmartDevice_Define";
|
||||||
|
$hash->{UndefFn} = "GardenaSmartDevice_Undef";
|
||||||
|
$hash->{ParseFn} = "GardenaSmartDevice_Parse";
|
||||||
|
|
||||||
|
$hash->{AttrFn} = "GardenaSmartDevice_Attr";
|
||||||
|
$hash->{AttrList} = "disable:1 ".
|
||||||
|
"model ".
|
||||||
|
$readingFnAttributes;
|
||||||
|
|
||||||
|
foreach my $d(sort keys %{$modules{GardenaSmartDevice}{defptr}}) {
|
||||||
|
|
||||||
|
my $hash = $modules{GardenaSmartDevice}{defptr}{$d};
|
||||||
|
$hash->{VERSION} = $version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Define($$) {
|
||||||
|
|
||||||
|
my ( $hash, $def ) = @_;
|
||||||
|
my @a = split( "[ \t]+", $def );
|
||||||
|
splice( @a, 1, 1 );
|
||||||
|
my $iodev;
|
||||||
|
my $i = 0;
|
||||||
|
|
||||||
|
|
||||||
|
foreach my $param ( @a ) {
|
||||||
|
if( $param =~ m/IODev=([^\s]*)/ ) {
|
||||||
|
|
||||||
|
$iodev = $1;
|
||||||
|
splice( @a, $i, 3 );
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return "too few parameters: define <NAME> GardenaSmartDevice <device_Id> <model>" if( @a != 3 ) ;
|
||||||
|
return "Cannot define Gardena Bridge device. Perl modul $missingModul is missing." if ( $missingModul );
|
||||||
|
|
||||||
|
my ($name,$deviceId,$category) = @a;
|
||||||
|
|
||||||
|
$hash->{DEVICEID} = $deviceId;
|
||||||
|
$hash->{VERSION} = $version;
|
||||||
|
|
||||||
|
AssignIoPort($hash,$iodev) if( !$hash->{IODev} );
|
||||||
|
|
||||||
|
if(defined($hash->{IODev}->{NAME})) {
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartDevice ($name) - I/O device is " . $hash->{IODev}->{NAME};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Log3 $name, 1, "GardenaSmartDevice ($name) - no I/O device";
|
||||||
|
}
|
||||||
|
|
||||||
|
$iodev = $hash->{IODev}->{NAME};
|
||||||
|
|
||||||
|
my $d = $modules{GardenaSmartDevice}{defptr}{$deviceId};
|
||||||
|
|
||||||
|
return "GardenaSmartDevice device $name on GardenaSmartBridge $iodev already defined."
|
||||||
|
if( defined($d) && $d->{IODev} == $hash->{IODev} && $d->{NAME} ne $name );
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartDevice ($name) - defined with DEVICEID: $deviceId";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$attr{$name}{room} = "GardenaSmart" if( not defined( $attr{$name}{room} ) );
|
||||||
|
$attr{$name}{model} = $category if( not defined( $attr{$name}{model} ) );
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartDevice ($name) - defined GardenaSmartDevice";
|
||||||
|
|
||||||
|
#GardenaSmartDevice_Open( $hash );
|
||||||
|
|
||||||
|
$modules{GardenaSmartDevice}{defptr}{$deviceId} = $hash;
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Undef($$) {
|
||||||
|
|
||||||
|
my ( $hash, $arg ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $deviceId = $hash->{DEVICEID};
|
||||||
|
|
||||||
|
|
||||||
|
delete $modules{GardenaSmartDevice}{defptr}{$deviceId};
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Attr(@) {
|
||||||
|
|
||||||
|
my ( $cmd, $name, $attrName, $attrVal ) = @_;
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
my $orig = $attrVal;
|
||||||
|
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Set($@) {
|
||||||
|
|
||||||
|
my ($hash, $name, $cmd, @args) = @_;
|
||||||
|
my ($arg, @params) = @args;
|
||||||
|
|
||||||
|
my $payload;
|
||||||
|
|
||||||
|
|
||||||
|
if( lc $cmd eq 'parkuntilfurthernotice' ) {
|
||||||
|
|
||||||
|
$payload = '"name":"park_until_further_notice"';
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq 'parkuntilnexttimer' ) {
|
||||||
|
|
||||||
|
$payload = '"name":"park_until_next_timer"';
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq 'startresumeschedule' ) {
|
||||||
|
|
||||||
|
$payload = '"name":"start_resume_schedule"';
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq 'startoverridetimer' ) {
|
||||||
|
|
||||||
|
my $duration = join( " ", @args );
|
||||||
|
$payload = '"name":"start_override_timer","parameters":{"duration":' . $duration . '}';
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq 'statusrequest' ) {
|
||||||
|
|
||||||
|
#$payload = '"name":"device_info"';
|
||||||
|
$payload = '"name":"measure_ambient_temperature"';
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
|
||||||
|
} elsif( lc $cmd eq '' ) {
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
my $list = 'statusRequest:noArg ';
|
||||||
|
$list .= 'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,1,1440' if( AttrVal($name,'model','unknown') eq 'mower' );
|
||||||
|
|
||||||
|
return "Unknown argument $cmd, choose one of $list";
|
||||||
|
}
|
||||||
|
|
||||||
|
IOWrite($hash,$payload,$hash->{DEVICEID},AttrVal($name,'model','unknown'));
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - IOWrite: $payload $hash->{DEVICEID} " . AttrVal($name,'model','unknown') . " IODevHash=$hash->{IODev}";
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_Parse($$) {
|
||||||
|
|
||||||
|
my ($io_hash,$json) = @_;
|
||||||
|
|
||||||
|
my $name = $io_hash->{NAME};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
my $decode_json = eval{decode_json($json)};
|
||||||
|
if($@){
|
||||||
|
Log3 $name, 3, "GardenaSmartBridge ($name) - JSON error while request: $@";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 $name, 4, "GardenaSmartDevice ($name) - ParseFn was called";
|
||||||
|
Log3 $name, 4, "GardenaSmartDevice ($name) - JSON: $json";
|
||||||
|
|
||||||
|
|
||||||
|
if( defined($decode_json->{id}) ) {
|
||||||
|
|
||||||
|
my $deviceId = $decode_json->{id};
|
||||||
|
|
||||||
|
if( my $hash = $modules{GardenaSmartDevice}{defptr}{$deviceId} ) {
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
GardenaSmartDevice_WriteReadings($hash,$decode_json);
|
||||||
|
Log3 $name, 3, "GardenaSmartDevice ($name) - find logical device: $hash->{NAME}";
|
||||||
|
|
||||||
|
return $hash->{NAME};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Log3 $name, 3, "GardenaSmartDevice ($name) - Wird angelegt NAME: $decode_json->{name} ID: $decode_json->{id} CATEGORY: $decode_json->{category} IODev=$name";
|
||||||
|
return "UNDEFINED $decode_json->{name} GardenaSmartDevice $decode_json->{id} $decode_json->{category} IODev=$name";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub GardenaSmartDevice_WriteReadings($$) {
|
||||||
|
|
||||||
|
my ($hash,$decode_json) = @_;
|
||||||
|
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $abilities = scalar (@{$decode_json->{abilities}});
|
||||||
|
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if( ref($decode_json->{abilities}[$abilities]{properties}) eq "ARRAY" and scalar(@{$decode_json->{abilities}[$abilities]{properties}}) > 0 ) {;
|
||||||
|
|
||||||
|
foreach my $propertie (@{$decode_json->{abilities}[$abilities]{properties}}) {
|
||||||
|
readingsBeginUpdate($hash);
|
||||||
|
readingsBulkUpdateIfChanged($hash,$decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name},$propertie->{value});
|
||||||
|
readingsEndUpdate( $hash, 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$abilities--;
|
||||||
|
} while ($abilities >= 0);
|
||||||
|
|
||||||
|
Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written}";
|
||||||
|
}
|
||||||
|
|
||||||
|
##################################
|
||||||
|
##################################
|
||||||
|
#### my little helpers ###########
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=item device
|
||||||
|
=item summary Gardena Smart
|
||||||
|
=item summary_DE Gardena Smart
|
||||||
|
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=end html
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
=cut
|
Loading…
x
Reference in New Issue
Block a user