From 37ef1dd7f45b464af9f14d895874e70e36bc9c54 Mon Sep 17 00:00:00 2001
From: LeonGaultier
Date: Mon, 4 Sep 2017 07:19:47 +0000
Subject: [PATCH] 73_GardenaSmartBridge: and 74_GardenaSmartDevice to control
your gardena smart products
git-svn-id: https://svn.fhem.de/fhem/trunk@15001 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/CHANGED | 2 +
fhem/FHEM/73_GardenaSmartBridge.pm | 878 +++++++++++++++++++++++++++++
fhem/FHEM/74_GardenaSmartDevice.pm | 813 ++++++++++++++++++++++++++
fhem/MAINTAINER.txt | 2 +
4 files changed, 1695 insertions(+)
create mode 100644 fhem/FHEM/73_GardenaSmartBridge.pm
create mode 100644 fhem/FHEM/74_GardenaSmartDevice.pm
diff --git a/fhem/CHANGED b/fhem/CHANGED
index 8d74f20a3..fb8549844 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - new: 73_GardenaSmartBridge/74_GardenaSmartDevice control your Gardena
+ Smart Products
- feature: 98_MediaList.pm: added sorting setting function,
default sort by filename
- feature: 10_pilight_ctrl: support protocol heitech
diff --git a/fhem/FHEM/73_GardenaSmartBridge.pm b/fhem/FHEM/73_GardenaSmartBridge.pm
new file mode 100644
index 000000000..9a225a694
--- /dev/null
+++ b/fhem/FHEM/73_GardenaSmartBridge.pm
@@ -0,0 +1,878 @@
+###############################################################################
+#
+# Developed with Kate
+#
+# (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
+# All rights reserved
+#
+# Special thanks goes to comitters:
+# - Michael (mbrak) Thanks for Commandref
+# - Matthias (Kenneth) Thanks for Wiki entry
+#
+#
+# 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 ";
+eval "use IO::Socket::SSL;1" or $missingModul .= "IO::Socket::SSL ";
+###todo Hier fehlt noch Modulabfrage für ssl
+
+
+my $version = "0.2.0";
+
+
+
+
+# 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 ".
+ "interval ".
+ $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 GardenaSmartBridge " 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);
+ readingsSingleUpdate($hash,'token','none',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 'gettoken' ) {
+
+ return "token is up to date" if( defined($hash->{helper}{session_id}) );
+ GardenaSmartBridge_getToken($hash);
+
+ } else {
+
+ my $list = "getDevicesState:noArg getToken: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,$abilities) = @_;
+ 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($abilities) and defined($hash->{helper}{locations_id}) );
+ }
+
+ $uri .= '/sessions' if( not defined($hash->{helper}{session_id}));
+
+ if( defined($hash->{helper}{locations_id}) ) {
+ $uri .= '/devices/' . $deviceId . '/abilities/' . $abilities . '/command' if( defined($abilities) and defined($payload) );
+ $uri .= '?locationId=' . $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};
+
+ my $dhash;
+ if( defined( $param->{'device_id'}) ) {
+ $dhash = $modules{GardenaSmartDevice}{defptr}{$param->{'device_id'}};
+ } else {
+ $dhash = $hash;
+ }
+ my $dname = $dhash->{NAME};
+
+
+ ###todo Das gesamte Errorhandling muss hier noch rein
+
+ #Log3 $name, 1, "GardenaSmartBridge ($name) - Header:\n".Dumper($param->{header});
+ #Log3 $name, 1, "GardenaSmartBridge ($name) - CODE:\n".Dumper($param->{code});
+ #Log3 $name, 1, "GardenaSmartBridge ($name) - Error:\n".Dumper($err);
+ #Log3 $name, 1, "GardenaSmartBridge ($name) - Data:\n".Dumper($data);
+
+
+
+
+ #### Ein Fehler der Behandelt werden muss
+ # Data:
+ # '
+ #
+ #
+ # Error 400 Bad Request
+ #
+ # HTTP ERROR 400
+ # Problem accessing /sg-1/devices/2ad0d816-8bc3-4f0a-8c52-8b0dc8d7b2ec/abilities/watering_computer/command. Reason:
+ #
Bad Request
Powered by Jetty://
+ #
+ #
+ #
+ # ';
+
+ # '
+ #
+ #
+ # Error 503 Service Unavailable
+ #
+ # HTTP ERROR 503
+ # Problem accessing /sg-1/devices/2ad0d816-8bc3-4f0a-8c52-8b0dc8d7b2ec/abilities/outlet/command. Reason:
+ #
Service Unavailable
Powered by Jetty://
+ #
+ #
+ #
+ # ';
+
+
+ # 2017.08.10 11:17:20 1: GardenaSmartBridge (myGardena) - Data:
+ # $VAR1 = '{"errors":[{"attribute":"password","error":"invalid"}]}';
+
+
+
+
+ if( defined( $err ) ) {
+ if( $err ne "" ) {
+
+ readingsBeginUpdate( $dhash );
+ readingsBulkUpdate( $dhash, "state", "$err") if( ReadingsVal( $dname, "state", 1 ) ne "initialized" );
+
+ readingsBulkUpdate( $dhash, "lastRequestState", "request_error", 1 );
+
+ if( $err =~ /timed out/ ) {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: connect to gardena cloud is timed out. check network";
+ }
+
+ elsif( $err =~ /Keine Route zum Zielrechner/ or $err =~ /no route to target/ ) {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: no route to target. bad network configuration or network is down";
+
+ } else {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: $err";
+ }
+
+ readingsEndUpdate( $dhash, 1 );
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: GardenaSmartBridge RequestErrorHandling: error while requesting gardena cloud: $err";
+
+ delete $dhash->{helper}{deviceAction} if( defined($dhash->{helper}{deviceAction}) );
+
+ return;
+ }
+ }
+
+ if( $data eq "" and exists( $param->{code} ) && $param->{code} != 200 ) {
+
+ readingsBeginUpdate( $dhash );
+ readingsBulkUpdate( $dhash, "state", $param->{code}, 1 ) if( ReadingsVal( $dname, "state", 1 ) ne "initialized" );
+
+ readingsBulkUpdateIfChanged( $dhash, "lastRequestState", "request_error", 1 );
+
+ if( $param->{code} == 401 and $hash eq $dhash ) {
+
+ if( ReadingsVal($dname,'token','none') eq 'none' ) {
+ readingsBulkUpdate( $dhash, "state", "no token available", 1);
+ readingsBulkUpdateIfChanged( $dhash, "lastRequestState", "no token available", 1 );
+ }
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: ".$param->{code};
+
+ } elsif( $param->{code} == 204 and $dhash ne $hash and defined($dhash->{helper}{deviceAction}) ) {
+
+ readingsBulkUpdate( $dhash, "state", "the command is processed", 1);
+ InternalTimer( gettimeofday()+3,"GardenaSmartBridge_getDevices", $hash, 1 );
+
+ } elsif( $param->{code} != 200 ) {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: ".$param->{code};
+ }
+
+ readingsEndUpdate( $dhash, 1 );
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: received http code ".$param->{code}." without any data after requesting gardena cloud";
+
+ delete $dhash->{helper}{deviceAction} if( defined($dhash->{helper}{deviceAction}) );
+
+ return;
+ }
+
+ if( ( ($data =~ /Error/ ) or defined(eval{decode_json($data)}->{errors}) ) and exists( $param->{code} ) ) {
+ readingsBeginUpdate( $dhash );
+ readingsBulkUpdate( $dhash, "state", $param->{code}, 1 ) if( ReadingsVal( $dname, "state" ,0) ne "initialized" );
+
+ readingsBulkUpdate( $dhash, "lastRequestState", "request_error", 1 );
+
+ if( $param->{code} == 400 ) {
+ if( eval{decode_json($data)} ) {
+ if( ref(eval{decode_json($data)}->{errors}) eq "ARRAY" and defined(eval{decode_json($data)}->{errors}) ) {
+ readingsBulkUpdate( $dhash, "state", eval{decode_json($data)}->{errors}[0]{error} . ' ' . eval{decode_json($data)}->{errors}[0]{attribute}, 1);
+ readingsBulkUpdate( $dhash, "lastRequestState", eval{decode_json($data)}->{errors}[0]{error} . ' ' . eval{decode_json($data)}->{errors}[0]{attribute}, 1 );
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: " . eval{decode_json($data)}->{errors}[0]{error} . " " . eval{decode_json($data)}->{errors}[0]{attribute};
+ }
+ } else {
+ readingsBulkUpdate( $dhash, "lastRequestState", "Error 400 Bad Request", 1 );
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: Error 400 Bad Request";
+ }
+ } elsif( $param->{code} == 503 ) {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: Error 503 Service Unavailable";
+ readingsBulkUpdate( $dhash, "state", "Service Unavailable", 1 );
+ readingsBulkUpdate( $dhash, "lastRequestState", "Error 503 Service Unavailable", 1 );
+
+ } elsif( $param->{code} == 404 ) {
+ if( defined($dhash->{helper}{deviceAction}) and $dhash ne $hash ) {
+ readingsBulkUpdate( $dhash, "state", "device Id not found", 1 );
+ readingsBulkUpdate( $dhash, "lastRequestState", "device id not found", 1 );
+ }
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: Error 404 Not Found";
+
+ } elsif( $param->{code} == 500 ) {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: check the ???";
+
+ } else {
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: http error ".$param->{code};
+ }
+
+ readingsEndUpdate( $dhash, 1 );
+
+ Log3 $dname, 5, "GardenaSmartBridge ($dname) - RequestERROR: received http code ".$param->{code}." receive Error after requesting gardena cloud";
+
+ delete $dhash->{helper}{deviceAction} if( defined($dhash->{helper}{deviceAction}) );
+
+ return;
+ }
+
+
+
+
+
+
+ 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";
+ readingsSingleUpdate($hash,'token',$hash->{helper}{session_id},1);
+
+ 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)
+ 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";
+
+ RemoveInternalTimer($hash);
+ InternalTimer( gettimeofday()+$hash->{INTERVAL},"GardenaSmartBridge_InternalTimerGetDeviceData", $hash, 1 );
+}
+
+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 Modul to communicate with the GardenaCloud
+=item summary_DE Modul zur Datenübertragung zur GardenaCloud
+
+=begin html
+
+
+GardenaSmartBridge
+
+ Prerequisite
+
+ - In combination with GardenaSmartDevice this FHEM Module controls the communication between the GardenaCloud and connected Devices like Mover, Watering_Computer, Temperature_Sensors
+ - Installation of the following packages: apt-get install libio-socket-ssl-perl
+ - The Gardena-Gateway and all connected Devices must be correctly installed in the GardenaAPP
+
+
+
+Define
+
+ define <name> GardenaSmartBridge <Account-EMail> <Account-Passwort>
+
+ Beispiel:
+
+ define Gardena_Bridge GardenaSmartBridge me@email.me secret
+
+
+ <Account-EMail> Email Adresse which was used in the GardenaAPP
+ <Account-Passwort> Passwort which was used in the GardenaAPP
+ The GardenaSmartBridge device is created in the room GardenaSmart, then the devices of Your system are recognized automatically and created in FHEM. From now on the devices can be controlled and changes in the GardenaAPP are synchronized with the state and readings of the devices.
+
+
+
+ Readings
+
+ - address - your Adress (Longversion)
+ - authorized_user_ids -
+ - city - Zip, City
+ - devices - Number of Devices in the Cloud (Gateway included)
+ - lastRequestState - Last Status Result
+ - latitude - Breitengrad des Grundstücks
+ - longitude - Längengrad des Grundstücks
+ - name - Name of your Garden – Default „My Garden“
+ - state - State of the Bridge
+ - token - SessionID
+ - zones -
+
+
+
+ set
+
+ - getDeviceState - Starts a Datarequest
+ - getToken - Gets a new Session-ID
+
+
+
+ Attributes
+
+ - debugJSON -
+ - disable - Disables the Bridge
+ - interval - Interval in Minutes (Default=5)
+
+
+
+=end html
+=begin html_DE
+
+
+GardenaSmartBridge
+
+ Voraussetzungen
+
+ - Zusammen mit dem Device GardenaSmartDevice stellt dieses FHEM Modul die Kommunikation zwischen der GardenaCloud und Fhem her. Es können damit Rasenmäher, Bewässerungscomputer und Bodensensoren überwacht und gesteuert werden
+ - Das Perl-Modul "SSL Packet" wird benötigt.
+ - Unter Debian (basierten) System, kann dies mittels "apt-get install libio-socket-ssl-perl" installiert werden.
+ - Das Gardena-Gateway und alle damit verbundenen Geräte und Sensoren müssen vorab in der GardenaApp eingerichtet sein.
+
+
+
+Define
+
+ define <name> GardenaSmartBridge <Account-EMail> <Account-Passwort>
+
+ Beispiel:
+
+ define Gardena_Bridge GardenaSmartBridge me@email.me secret
+
+
+ <Account-EMail> Email Adresse, die auch in der GardenaApp verwendet wurde
+ <Account-Passwort> Passwort, welches in der GardenaApp verwendet wurde
+ Das Bridge Device wird im Raum GardenaSmart angelegt und danach erfolgt das Einlesen und automatische Anlegen der Geräte. Von nun an können die eingebundenen Geräte gesteuert werden. Änderungen in der APP werden mit den Readings und dem Status syncronisiert.
+
+
+
+ Readings
+
+ - address - Adresse, welche in der App eingetragen wurde (Langversion)
+ - authorized_user_ids -
+ - city - PLZ, Stadt
+ - devices - Anzahl der Geräte, welche in der GardenaCloud angemeldet sind (Gateway zählt mit)
+ - lastRequestState - Letzter abgefragter Status der Bridge
+ - latitude - Breitengrad des Grundstücks
+ - longitude - Längengrad des Grundstücks
+ - name - Name für das Grundstück – Default „My Garden“
+ - state - Status der Bridge
+ - token - SessionID
+ - zones -
+
+
+
+ set
+
+ - getDeviceState - Startet eine Abfrage der Daten.
+ - getToken - Holt eine neue Session-ID
+
+
+
+ Attribute
+
+ - debugJSON - JSON Fehlermeldungen
+ - disable - Schaltet die Datenübertragung der Bridge ab
+ - interval - Abfrageinterval in Sekunden (default: 300)
+
+
+
+=end html_DE
+=cut
diff --git a/fhem/FHEM/74_GardenaSmartDevice.pm b/fhem/FHEM/74_GardenaSmartDevice.pm
new file mode 100644
index 000000000..86274ad30
--- /dev/null
+++ b/fhem/FHEM/74_GardenaSmartDevice.pm
@@ -0,0 +1,813 @@
+###############################################################################
+#
+# Developed with Kate
+#
+# (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
+# All rights reserved
+#
+# Special thanks goes to comitters:
+# - Michael (mbrak) Thanks for Commandref
+# - Matthias (Kenneth) Thanks for Wiki entry
+#
+#
+# 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 Time::Local;
+
+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.2.0";
+
+
+
+
+# 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_ReadingLangGerman($$);
+sub GardenaSmartDevice_RigRadingsValue($$);
+sub GardenaSmartDevice_Zulu2LocalString($);
+
+
+
+
+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} = "readingValueLanguage:de,en ".
+ "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 GardenaSmartDevice " 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 );
+
+ $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 with DEVICEID: $deviceId";
+ readingsSingleUpdate($hash,'state','initialized',1);
+
+ $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};
+
+
+ return undef;
+}
+
+sub GardenaSmartDevice_Set($@) {
+
+ my ($hash, $name, $cmd, @args) = @_;
+ my ($arg, @params) = @args;
+
+ my $payload;
+ my $abilities;
+
+
+ ### mower
+ 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 . '}';
+
+ ### watering_computer
+ } elsif( lc $cmd eq 'manualoverride' ) {
+
+ my $duration = join( " ", @args );
+ $payload = '"name":"manual_override","parameters":{"duration":' . $duration . '}';
+
+ } elsif( lc $cmd eq 'canceloverride' ) {
+
+ $payload = '"name":"cancel_override"';
+
+ ### Sensors
+ } elsif( lc $cmd eq 'refresh' ) {
+
+ my $sensname = join( " ", @args );
+ if( lc $sensname eq 'temperature' ) {
+ $payload = '"name":"measure_ambient_temperature"';
+ $abilities = 'ambient_temperature';
+
+ } elsif( lc $sensname eq 'light' ) {
+ $payload = '"name":"measure_light"';
+ $abilities = 'light';
+
+ } elsif( lc $sensname eq 'humidity' ) {
+ $payload = '"name":"measure_humidity"';
+ $abilities = 'humidity';
+ }
+
+ } 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 = '';
+ $list .= 'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,60,1440' if( AttrVal($name,'model','unknown') eq 'mower' );
+ $list .= 'manualOverride:slider,0,1,59 cancelOverride:noArg' if( AttrVal($name,'model','unknown') eq 'watering_computer' );
+ $list .= 'refresh:temperature,light' if( AttrVal($name,'model','unknown') eq 'sensor' );
+
+ return "Unknown argument $cmd, choose one of $list";
+ }
+
+ $abilities = 'mower' if( AttrVal($name,'model','unknown') eq 'mower' );
+ $abilities = 'outlet' if( AttrVal($name,'model','unknown') eq 'watering_computer' );
+
+
+ $hash->{helper}{deviceAction} = $payload;
+ readingsSingleUpdate( $hash, "state", "send command to gardena cloud", 1);
+
+ IOWrite($hash,$payload,$hash->{DEVICEID},$abilities);
+ Log3 $name, 4, "GardenaSmartBridge ($name) - IOWrite: $payload $hash->{DEVICEID} $abilities 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, 5, "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, 4, "GardenaSmartDevice ($name) - find logical device: $hash->{NAME}";
+
+ return $hash->{NAME};
+
+ } else {
+
+ Log3 $name, 3, "GardenaSmartDevice ($name) - autocreate new device " . join('',split("[ \t]+",$decode_json->{name})) . " with deviceId $decode_json->{id}, model $decode_json->{category} and IODev IODev=$name";
+ return "UNDEFINED " . join('',split("[ \t]+",$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}});
+
+
+ readingsBeginUpdate($hash);
+
+ 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}}) {
+ readingsBulkUpdateIfChanged($hash,$decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name},GardenaSmartDevice_RigRadingsValue($hash,$propertie->{value})) if( defined($propertie->{value})
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'radio-quality'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'battery-level'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'internal_temperature-temperature'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'ambient_temperature-temperature'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'soil_temperature-temperature'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'humidity-humidity'
+ and $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} ne 'light-light' );
+
+ readingsBulkUpdate($hash,$decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name},GardenaSmartDevice_RigRadingsValue($hash,$propertie->{value})) if( defined($propertie->{value})
+ and ($decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'radio-quality'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'battery-level'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'internal_temperature-temperature'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'ambient_temperature-temperature'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'soil_temperature-temperature'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'humidity-humidity'
+ or $decode_json->{abilities}[$abilities]{name}.'-'.$propertie->{name} eq 'light-light') );
+ }
+ }
+
+ $abilities--;
+ } while ($abilities >= 0);
+
+
+ readingsBulkUpdate($hash,'state',ReadingsVal($name,'mower-status','readingsValError')) if( AttrVal($name,'model','unknown') eq 'mower' );
+ readingsBulkUpdate($hash,'state',(ReadingsVal($name,'outlet-valve_open','readingsValError') == 1 ? GardenaSmartDevice_RigRadingsValue($hash,'open') : GardenaSmartDevice_RigRadingsValue($hash,'closed'))) if( AttrVal($name,'model','unknown') eq 'watering_computer' );
+
+ readingsBulkUpdate($hash,'state','T: ' . ReadingsVal($name,'ambient_temperature-temperature','readingsValError') . '°C, H: ' . ReadingsVal($name,'humidity-humidity','readingsValError') . '%, L: ' . ReadingsVal($name,'light-light','readingsValError') . 'lux') if( AttrVal($name,'model','unknown') eq 'sensor' );
+
+ readingsEndUpdate( $hash, 1 );
+
+ Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written}";
+}
+
+##################################
+##################################
+#### my little helpers ###########
+
+sub GardenaSmartDevice_ReadingLangGerman($$) {
+
+ my ($hash,$readingValue) = @_;
+ my $name = $hash->{NAME};
+
+
+ my %langGermanMapp = (
+ 'ok_cutting' => 'mähen',
+ 'paused' => 'pausiert',
+ 'ok_searching' => 'suche Ladestation',
+ 'ok_charging' => 'lädt',
+ 'ok_leaving' => 'mähen',
+ 'wait_updating' => 'wird aktualisiert ...',
+ 'wait_power_up' => 'wird eingeschaltet ...',
+ 'parked_timer' => 'geparkt nach Zeitplan',
+ 'parked_park_selected' => 'geparkt',
+ 'off_disabled' => 'der Mäher ist ausgeschaltet',
+ 'off_hatch_open' => 'deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich',
+ 'unknown' => 'unbekannter Status',
+ 'error' => 'fehler',
+ 'error_at_power_up' => 'neustart ...',
+ 'off_hatch_closed' => 'deaktiviert. Manueller Start erforderlich',
+ 'ok_cutting_timer_overridden' => 'manuelles mähen',
+ 'parked_autotimer' => 'geparkt durch SensorControl',
+ 'parked_daily_limit_reached' => 'abgeschlossen',
+ 'no_message' => 'kein Fehler',
+ 'outside_working_area' => 'außerhalb des Arbeitsbereichs',
+ 'no_loop_signal' => 'kein Schleifensignal',
+ 'wrong_loop_signal' => 'falsches Schleifensignal',
+ 'loop_sensor_problem_front' => 'problem Schleifensensor, vorne',
+ 'loop_sensor_problem_rear' => 'problem Schleifensensor, hinten',
+ 'trapped' => 'eingeschlossen',
+ 'upside_down' => 'steht auf dem Kopf',
+ 'low_battery' => 'niedriger Batteriestand',
+ 'empty_battery' => 'empty_battery',
+ 'no_drive' => 'no_drive',
+ 'lifted' => 'angehoben',
+ 'stuck_in_charging_station' => 'eingeklemmt in Ladestation',
+ 'charging_station_blocked' => 'ladestation blockiert',
+ 'collision_sensor_problem_rear' => 'problem Stoßsensor hinten',
+ 'collision_sensor_problem_front' => 'problem Stoßsensor vorne',
+ 'wheel_motor_blocked_right' => 'radmotor rechts blockiert',
+ 'wheel_motor_blocked_left' => 'radmotor links blockiert',
+ 'wheel_drive_problem_right' => 'problem Antrieb, rechts',
+ 'wheel_drive_problem_left' => 'problem Antrieb, links',
+ 'cutting_system_blocked' => 'schneidsystem blockiert',
+ 'invalid_sub_device_combination' => 'Fehlerhafte Verbindung',
+ 'settings_restored' => 'standardeinstellungen',
+ 'electronic_problem' => 'elektronisches Problem',
+ 'charging_system_problem' => 'problem Ladesystem',
+ 'tilt_sensor_problem' => 'kippsensorproblem',
+ 'wheel_motor_overloaded_right' => 'rechter Radmotor überlastet',
+ 'wheel_motor_overloaded_left' => 'linker Radmotor überlastet',
+ 'charging_current_too_high' => 'ladestrom zu hoch',
+ 'temporary_problem' => 'vorübergehendes Problem',
+ 'guide_1_not_found' => 'sk 1 nicht gefunden',
+ 'guide_2_not_found' => 'sk 2 nicht gefunden',
+ 'guide_3_not_found' => 'sk 3 nicht gefunden',
+ 'difficult_finding_home' => 'problem die Ladestation zu finden',
+ 'guide_calibration_accomplished' => 'kalibration des Suchkabels beendet',
+ 'guide_calibration_failed' => 'kalibration des Suchkabels fehlgeschlagen',
+ 'temporary_battery_problem' => 'kurzzeitiges Batterieproblem',
+ 'battery_problem' => 'batterieproblem',
+ 'alarm_mower_switched_off' => 'alarm! Mäher ausgeschalten',
+ 'alarm_mower_stopped' => 'alarm! Mäher gestoppt',
+ 'alarm_mower_lifted' => 'alarm! Mäher angehoben',
+ 'alarm_mower_tilted' => 'alarm! Mäher gekippt',
+ 'connection_changed' => 'verbindung geändert',
+ 'connection_not_changed' => 'verbindung nicht geändert',
+ 'com_board_not_available' => 'com board nicht verfügbar',
+ 'slipped' => 'rutscht',
+ 'out_of_operation' => 'ausser Betrieb',
+ 'replace_now' => 'kritischer Batteriestand, wechseln Sie jetzt',
+ 'low' => 'niedrig',
+ 'ok' => 'oK',
+ 'no_source' => 'oK',
+ 'mower_charging' => 'mäher wurde geladen',
+ 'completed_cutting_autotimer' => 'sensorControl erreicht',
+ 'week_timer' => 'wochentimer erreicht',
+ 'countdown_timer' => 'stoppuhr Timer',
+ 'undefined' => 'unklar',
+ 'unknown' => 'unklar',
+ 'status_device_unreachable' => 'gerät ist nicht in Reichweite',
+ 'status_device_alive' => 'gerät ist in Reichweite',
+ 'bad' => 'schlecht',
+ 'poor' => 'schwach',
+ 'good' => 'gut',
+ 'undefined' => 'unklar',
+ 'idle' => 'nichts zu tun',
+ 'firmware_cancel' => 'firmwareupload unterbrochen',
+ 'firmware_upload' => 'firmwareupload',
+ 'unsupported' => 'nicht unterstützt',
+ 'up_to_date' => 'auf dem neusten Stand',
+ 'mower' => 'mäher',
+ 'watering_computer' => 'bewässerungscomputer',
+ 'no_frost' => 'kein Frost',
+ 'open' => 'offen',
+ 'closed' => 'geschlossen',
+ 'included' => 'inbegriffen',
+ 'active' => 'aktiv',
+ 'inactive' => 'nicht aktiv'
+ );
+
+ if( defined($langGermanMapp{$readingValue}) and (AttrVal('global','language','none') eq 'DE' or AttrVal($name,'readingValueLanguage','none') eq 'de') and AttrVal($name,'readingValueLanguage','none') ne 'en') {
+ return $langGermanMapp{$readingValue};
+ } else {
+ return $readingValue;
+ }
+}
+
+sub GardenaSmartDevice_RigRadingsValue($$) {
+
+ my ($hash,$readingValue) = @_;
+
+ my $rigReadingValue;
+
+
+ if( $readingValue =~ /^(\d+)-(\d\d)-(\d\d)T(\d\d)/ ) {
+ $rigReadingValue = GardenaSmartDevice_Zulu2LocalString($readingValue);
+ } else {
+ $rigReadingValue = GardenaSmartDevice_ReadingLangGerman($hash,$readingValue);
+ }
+
+ return $rigReadingValue;
+}
+
+sub GardenaSmartDevice_Zulu2LocalString($) {
+
+ my $t = shift;
+ my ($datehour,$datemin,$rest) = split(/:/,$t,3);
+
+
+ my ($year, $month, $day, $hour,$min) = $datehour =~ /(\d+)-(\d\d)-(\d\d)T(\d\d)/;
+ my $epoch = timegm (0,0,$hour,$day,$month-1,$year);
+
+ my ($lyear,$lmonth,$lday,$lhour,$isdst) = (localtime($epoch))[5,4,3,2,-1];
+
+ $lyear += 1900; # year is 1900 based
+ $lmonth++; # month number is zero based
+
+ if( defined($rest) ) {
+ return ( sprintf("%04d-%02d-%02d %02d:%02d:%s",$lyear,$lmonth,$lday,$lhour,$datemin,substr($rest,0,2)));
+ } elsif( $lyear < 2000 ) {
+ return "illegal year";
+ } else {
+ return ( sprintf("%04d-%02d-%02d %02d:%02d",$lyear,$lmonth,$lday,$lhour,substr($datemin,0,2)));
+ }
+}
+
+
+
+
+
+
+1;
+
+=pod
+
+=item device
+=item summary Modul to control GardenaSmart Devices
+=item summary_DE Modul zur Steuerung von GardenaSmartgeräten
+
+=begin html
+
+
+GardenaSmartDevice
+
+ In combination with GardenaSmartBridge this FHEM Module controls the GardenaSmart Device using the GardenaCloud
+
+ Once the Bridge device is created, the connected devices are automatically recognized and created in FHEM.
+ From now on the devices can be controlled and changes in the GardenaAPP are synchronized with the state and readings of the devices.
+
+
+ Readings
+
+ - battery-charging - Indicator if the Battery is charged (0/1) or with newer Firmware (false/true)
+ - battery-level - load percentage of the Battery
+ - battery-rechargeable_battery_status - healthyness of the battery (out_of_operation/replace_now/low/ok)
+ - device_info-category - category of device (mower/watering_computer)
+ - device_info-last_time_online - timestamp of last radio contact
+ - device_info-manufacturer - manufacturer
+ - device_info-product - product type
+ - device_info-serial_number - serial number
+ - device_info-sgtin -
+ - device_info-version - firmware version
+ - firmware-firmware_command - firmware command (idle/firmware_cancel/firmware_upload/unsupported)
+ - firmware-firmware_status - firmware status
+ - firmware-firmware_update_start - indicator when a firmwareupload is started
+ - firmware-firmware_upload_progress - progress indicator of firmware update
+ - firmware-inclusion_status - inclusion status
+ - internal_temperature-temperature - internal device temperature
+ - mower-error - actual error message
+
+ - no_message
+ - outside_working_area
+ - no_loop_signal
+ - wrong_loop_signal
+ - loop_sensor_problem_front
+ - loop_sensor_problem_rear
+ - trapped
+ - upside_down
+ - low_battery
+ - empty_battery
+ - no_drive
+ - lifted
+ - stuck_in_charging_station
+ - charging_station_blocked
+ - collision_sensor_problem_rear
+ - collision_sensor_problem_front
+ - wheel_motor_blocked_right
+ - wheel_motor_blocked_left
+ - wheel_drive_problem_right
+ - wheel_drive_problem_left
+ - cutting_system_blocked
+ - invalid_sub_device_combination
+ - settings_restored
+ - electronic_problem
+ - charging_system_problem
+ - tilt_sensor_problem
+ - wheel_motor_overloaded_right
+ - wheel_motor_overloaded_left
+ - charging_current_too_high
+ - temporary_problem
+ - guide_1_not_found
+ - guide_2_not_found
+ - guide_3_not_found
+ - difficult_finding_home
+ - guide_calibration_accomplished
+ - guide_calibration_failed
+ - temporary_battery_problem
+ - battery_problem
+ - alarm_mower_switched_off
+ - alarm_mower_stopped
+ - alarm_mower_lifted
+ - alarm_mower_tilted
+ - connection_changed
+ - connection_not_changed
+ - com_board_not_available
+ - slipped
+
+
+ - mower-manual_operation - (0/1) or with newer Firmware (false/true)
+ - mower-override_end_time - manual override end time
+ - mower-source_for_next_start - source for the next start
+
+ - no_source
+ - mower_charging
+ - completed_cutting_autotimer
+ - week_timer
+ - countdown_timer
+ - undefined
+
+
+ - mower-status - mower state (see state)
+ - mower-timestamp_next_start - timestamp of next scheduled start
+ - radio-connection_status - state of connection
+ - radio-quality - percentage of the radio quality
+ - radio-state - radio state (bad/poor/good/undefined)
+ - state - state of the mower
+
+ - paused
+ - ok_cutting
+ - ok_searching
+ - ok_charging
+ - ok_leaving
+ - wait_updating
+ - wait_power_up
+ - parked_timer
+ - parked_park_selected
+ - off_disabled
+ - off_hatch_open
+ - unknown
+ - error
+ - error_at_power_up
+ - off_hatch_closed
+ - ok_cutting_timer_overridden
+ - parked_autotimer
+ - parked_daily_limit_reached
+
+
+
+
+
+ Attributes
+
+ - readingValueLanguage - Change the Language of Readings (de,en/if not set the default is english and the global language is not set at german)
+ - model -
+
+
+
+ set
+
+ - parkUntilFurtherNotice
+ - parkUntilNextTimer
+ - startOverrideTimer - 0 to 59 Minutes
+ - startResumeSchedule
+
+
+
+=end html
+=begin html_DE
+
+
+GardenaSmartDevice
+
+ Zusammen mit dem Device GardenaSmartDevice stellt dieses FHEM Modul die Kommunikation zwischen der GardenaCloud und Fhem her.
+
+ Wenn das GardenaSmartBridge Device erzeugt wurde, werden verbundene Geräte automatisch erkannt und in Fhem angelegt.
+ Von nun an können die eingebundenen Geräte gesteuert werden. Änderungen in der APP werden mit den Readings und dem Status syncronisiert.
+
+
+
+
+ Readings
+
+ - battery-charging - Ladeindikator (0/1) oder mit neuerer Firmware (false/true)
+ - battery-level - Ladezustand der Batterie in Prozent
+ - battery-rechargeable_battery_status - Zustand der Batterie (Ausser Betrieb/Kritischer Batteriestand, wechseln Sie jetzt/Niedrig/oK)
+ - device_info-category - Eigenschaft des GerÄtes (Mäher/Bewässerungscomputer/Bodensensor)
+ - device_info-last_time_online - Zeitpunkt der letzten Funkübertragung
+ - device_info-manufacturer - Hersteller
+ - device_info-product - Produkttyp
+ - device_info-serial_number - Seriennummer
+ - device_info-sgtin -
+ - device_info-version - Firmware Version
+ - firmware-firmware_command - Firmware Kommando (Nichts zu tun/Firmwareupload unterbrochen/Firmwareupload/nicht unterstützt)
+ - firmware-firmware_status - Firmware Status
+ - firmware-firmware_update_start - Firmwareupdate (0/1) oder mit neuerer Firmware (false/true)
+ - firmware-firmware_upload_progress - Firmwareupdatestatus in Prozent
+ - firmware-inclusion_status - Einbindungsstatus
+ - internal_temperature-temperature - Interne Geräte Temperatur
+ - mower-error - Aktuelle Fehler Meldung
+
+ - Kein Fehler
+ - Au&szl;igerhalb des Arbeitsbereichs
+ - Kein Schleifensignal
+ - Falsches Schleifensignal
+ - Problem Schleifensensor, vorne
+ - Problem Schleifensensor, hinten
+ - Eingeschlossen
+ - Steht auf dem Kopf
+ - Niedriger Batteriestand
+ - Batterie ist leer
+ - Kein Antrieb
+ - Angehoben
+ - Eingeklemmt in Ladestation
+ - Ladestation blockiert
+ - Problem Sto&szl;igsensor hinten
+ - Problem Sto&szl;igsensor vorne
+ - Radmotor rechts blockiert
+ - Radmotor links blockiert
+ - Problem Antrieb, rechts
+ - Problem Antrieb, links
+ - Schneidsystem blockiert
+ - Fehlerhafte Verbindung
+ - Standardeinstellungen
+ - Elektronisches Problem
+ - Problem Ladesystem
+ - Kippsensorproblem
+ - Rechter Radmotor überlastet
+ - Linker Radmotor überlastet
+ - Ladestrom zu hoch
+ - Vorübergehendes Problem
+ - SK 1 nicht gefunden
+ - SK 2 nicht gefunden
+ - SK 3 nicht gefunden
+ - Problem die Ladestation zu finden
+ - Kalibration des Suchkabels beendet
+ - Kalibration des Suchkabels fehlgeschlagen
+ - Kurzzeitiges Batterieproblem
+ - Batterieproblem
+ - Alarm! Mäher ausgeschalten
+ - Alarm! Mäher gestoppt
+ - Alarm! Mäher angehoben
+ - Alarm! Mäher gekippt
+ - Verbindung geändert
+ - Verbindung nicht geändert
+ - COM board nicht verfügbar
+ - Rutscht
+
+
+ - mower-manual_operation - Manueller Betrieb (0/1) oder mit neuerer Firmware (false/true)
+ - mower-override_end_time - Zeitpunkt wann der manuelle Betrieb beendet ist
+ - mower-source_for_next_start - Grudn für den nächsten Start
+
+ - Kein Grund
+ - Mäher wurde geladen
+ - SensorControl erreicht
+ - Wochentimer erreicht
+ - Stoppuhr Timer
+ - Undefiniert
+
+
+ - mower-status - Mäher Status (siehe state
+ - mower-timestamp_next_start - Zeitpunkt des nächsten geplanten Starts
+ - radio-connection_status - Status der Funkverbindung
+ - radio-quality - Indikator für die Funkverbindung in Prozent
+ - radio-state - radio state (schlecht/schwach/gut/Undefiniert)
+ - state - Staus des Mähers
+
+ - Pausiert
+ - Mähen
+ - Suche Ladestation
+ - Lädt
+ - Mähen
+ - Wird aktualisiert ...
+ - Wird eingeschaltet ...
+ - Geparkt nach Zeitplan
+ - Geparkt
+ - Der Mäher ist ausgeschaltet
+ - Deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich
+ - Unbekannter Status
+ - Fehler
+ - Neustart ...
+ - Deaktiviert. Manueller Start erforderlich
+ - Manuelles Mähen
+ - Geparkt durch SensorControl
+ - Abgeschlossen
+
+
+
+
+
+ Attribute
+
+ - readingValueLanguage - Änderung der Sprache der Readings (de,en/wenn nichts gesetzt ist, dann Englisch es sei denn deutsch ist als globale Sprache gesetzt)
+ - model -
+
+
+ set
+
+ - parkUntilFurtherNotice - Parken des Mähers unter Umgehung des Zeitplans
+ - parkUntilNextTimer - Parken bis zum nächsten Zeitplan
+ - startOverrideTimer - Manuelles mähen (0 bis 59 Minuten)
+ - startResumeSchedule - Weiterführung des Zeitplans
+
+
+
+=end html_DE
+=cut
diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index fe5a84371..7c32d0a80 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -314,8 +314,10 @@ FHEM/73_km200.pm Sailor http://forum.fhem.de/index.php
FHEM/73_PRESENCE.pm markusbloch http://forum.fhem.de Unterstuetzende Dienste
FHEM/73_MPD.pm Wzut http://forum.fhem.de Multimedia
FHEM/73_AMADCommBridge.pm CoolTux http://forum.fhem.de Sonstige Systeme
+FHEM/73_GardenaSmartBridge.pm CoolTux http://forum.fhem.de Sonstige Systeme
FHEM/74_NUKIBridge.pm CoolTux http://forum.fhem.de Sonstige Systeme
FHEM/74_AMADDevice.pm CoolTux http://forum.fhem.de Sonstige Systeme
+FHEM/74_GardenaSmartDevice.pm CoolTux http://forum.fhem.de Sonstige Systeme
FHEM/74_HOMBOT.pm CoolTux http://forum.fhem.de sonstige Systeme
FHEM/74_NUKIDevice.pm CoolTux http://forum.fhem.de Sonstige Systeme
FHEM/74_Nmap.pm igami http://forum.fhem.de Unterstuetzende Dienste