#!/usr/bin/env perl #=============================================================================== # # FILE: 98_TadoAPI_API.pm # # USAGE: Module for FHEM # Info: Turn $debug on for debugging # # REQUIREMENTS: Below modules should be pre-installed. # HTTP::Request::Common # HTTP::Headers # LWP::UserAgent::Paranoid # Data::Dumper; # JSON # BUGS: --- # NOTES: --- # AUTHOR: Philipp Wolfmajer # ORGANIZATION: # VERSION: 1.0 # CREATED: 04/12/2019 07:55:44 PM # REVISION: 04/23/2019 05:17:22 PM #=============================================================================== package main; use strict; use warnings; use utf8; use FHEM::Meta; use HTTP::Request::Common qw (POST GET PUT); use HTTP::Headers; use LWP::UserAgent::Paranoid; use JSON; ####DEFAULTS############ my $client_id='public-api-preview'; my $client_secret='4HJGRffVR8xb3XdEUQpjgZ1VplJi6Xgw'; my $scope='home.user'; my $AuthURL = qq{https://auth.tado.com/oauth/token}; my $DataURL = qq{https://my.tado.com/api/v2/me}; my $QueryURL = qq{https://my.tado.com/api/v2/homes}; my $tokenFile = "./FHEM/FhemUtils/TadoAPI_token"; my $debug = 0; my $header = {}; my $data = {}; my $TokenData = {}; my $apiStatus = 1; ##################################### #Request data for HTTP Request & Response my $Request=undef; my $Response=undef; #Empty variable for LWP User Agent for sending HTTP Request my $UserAgent = undef; my %sets = ( "getTemperature" => "noArg", "refreshToken" => "noArg", "password" => "", "setGeo" => "on,off" ); my %gets = ( "getHomeMode" => "noArg", "getZoneInfo" => "noArg", "getGeo" => "noArg" ); sub TadoAPI_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "TadoAPI_Define"; $hash->{InitFn} = "TadoAPI_Init"; $hash->{SetFn} = "TadoAPI_Set"; $hash->{GetFn} = "TadoAPI_Get"; $hash->{AttrList} = "homeID " . "mobileID " . "debug:1,0 " . $main::readingFnAttributes; } sub TadoAPI_Init($$) { my ($hash,$args) = @_; my $u = "wrong syntax: define TadoAPI []"; return $u if(int(@$args) < 2); return undef; } sub TadoAPI_Define($$) { my ($hash, $def) = @_; my @a = split( "[ \t]+", $def ); my $name = shift @a; my $type = shift @a; return "Invalid number of arguments: " . "define TadoAPI []" if ( int(@a) < 2 ); my ( $user, $homeID, $mobileID ) = @a; Log3 $name, 3, "TadoAPI_Define $name: called "; $hash->{STATE}="defined"; # Initialize the device return $@ unless ( FHEM::Meta::SetInternals($hash) ); $hash->{TADO_USER} = $user; if ( defined($homeID) && $homeID ne "" ) { $attr{$name}{homeID} = $homeID; } if ( defined($mobileID) && $mobileID ne "" ) { $main::attr{$a[0]}{"mobileID"}= $mobileID; } my @args = ($homeID, $mobileID); if ($main::init_done) { # do something? return TadoAPI_Catch($@) if $@; } # start the status update timer RemoveInternalTimer($hash); InternalTimer( gettimeofday() + 10, "TadoAPI_Update", $hash, 0 ); return undef; } sub TadoAPI_Set(@) { my ($hash, @a) = @_; return "Need at least one parameters" if(@a < 2); my $cmd = $a[1]; my $value = $a[2]; my $name = $hash->{NAME}; my $subcmd; #debug $debug = $attr{$name}{debug}; if(!defined($sets{$cmd})) { my @cmds = (); foreach my $key (sort keys %sets) { push @cmds, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key; } return "Unknown argument $a[1], choose one of " . join(" ", @cmds); } if( $cmd eq 'setGeo' ) { if( $value eq "on" ) { Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)\n"; TadoAPI_SetGeo($hash, 1); } else { Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)\n"; TadoAPI_SetGeo($hash, 0); } TadoAPI_GetGeo($hash); return undef; } elsif( $cmd eq 'refreshToken' ) { Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)\n"; RemoveInternalTimer($hash); InternalTimer( gettimeofday() + 10, "TadoAPI_Update", $hash, 0 ); TadoAPI_Connect($hash); return undef; } elsif( $cmd eq 'getTemperature' ) { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; TadoAPI_GetTemperature($hash); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; return undef; } elsif( $cmd eq 'password' ) { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; # name und cmd überspringen shift @a; shift @a; # den Rest der das passwort enthält, als ein String $subcmd = join(" ",@a); return TadoAPI_storePassword($name,$subcmd); #Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; return undef; } return TadoAPI_Catch($@) if $@; } sub TadoAPI_Get(@) { my ($hash, @a) = @_; return "Need at least one parameters" if(@a < 2); my $cmd = $a[1]; my $value = $a[2]; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $mobileID = $attr{$name}{mobileID}; #debug $debug = $attr{$name}{debug}; if(!defined($gets{$cmd})) { my @cmds = (); foreach my $key (sort keys %gets) { push @cmds, $gets{$key} ? $key.":".join(",",$gets{$key}) : $key; } return "Unknown argument $a[1], choose one of " . join(" ", @cmds); } if($cmd =~ /\Qget\E/) { eval { COMMAND_HANDLER: { $cmd eq "getGeo" and do { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; TadoAPI_GetGeo($hash); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; last; }; $cmd eq "getHomeMode" and do { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; TadoAPI_GetHomeMode($hash); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; last; }; $cmd eq "getZoneInfo" and do { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; TadoAPI_GetZoneInfo($hash); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; last; }; } }; return TadoAPI_Catch($@) if $@; return undef; } } sub TadoAPI_Catch($) { my $exception = shift; if ($exception) { $exception =~ /^(.*)( at.*FHEM.*)$/; return $1; } return undef; } sub TadoAPI_Undefine($$) { my ( $hash, $name ) = @_; RemoveInternalTimer($hash); #todo remove tokenfile return undef; } ###################################################### # storePW & readPW Code geklaut aus 96_SIP.pm :) ###################################################### sub TadoAPI_storePassword($$) { my ($name, $password) = @_; my $index = "TadoAPI_".$name."_passwd"; my $key = getUniqueId().$index; my $e_pwd = ""; if (eval "use Digest::MD5;1") { $key = Digest::MD5::md5_hex(unpack "H*", $key); $key .= Digest::MD5::md5_hex($key); } for my $char (split //, $password) { my $encode=chop($key); $e_pwd.=sprintf("%.2x",ord($char)^ord($encode)); $key=$encode.$key; } my $error = setKeyValue($index, $e_pwd); return "error while saving TadoAPI password : $error" if(defined($error)); return "TadoAPI password successfully saved in FhemUtils/uniqueID Key $index"; } sub TadoAPI_readPassword($) { my ($name) = @_; my $index = "TadoAPI_".$name."_passwd"; my $key = getUniqueId().$index; my ($password, $error); #Log3 $name,5,"$name, read user password from FhemUtils/uniqueID Key $key"; ($error, $password) = getKeyValue($index); if ( defined($error) ) { Log3 $name,3, "$name, cant't read Tado password from FhemUtils/uniqueID: $error"; return undef; } if ( defined($password) ) { if (eval "use Digest::MD5;1") { $key = Digest::MD5::md5_hex(unpack "H*", $key); $key .= Digest::MD5::md5_hex($key); } my $dec_pwd = ''; for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g)) { my $decode=chop($key); $dec_pwd.=chr(ord($char)^ord($decode)); $key=$decode.$key; } return $dec_pwd; } else { Log3 $name,3,"$name, no Tado password found in FhemUtils/uniqueID"; return undef; } } sub TadoAPI_callback($$$){ my ($param, $err, $data) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; if($param->{code} == 401 || $param->{code} == 400){ $apiStatus = 1; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "API-Status", "reachable"); readingsEndUpdate( $hash, 1 ); Log3 $name, 3, "TadoAPI $name" . ": " . "API is reachable. Callback Status: " . $param->{code} if $debug; }else{ $apiStatus = 0; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "API-Status", "offline"); readingsEndUpdate( $hash, 1 ); Log3 $name, 3, "TadoAPI $name" . ": " . "API error: apiStatus $apiStatus ($err)" if $debug; } return undef; } sub TadoAPI_Status(@){ my ($hash) = @_; my $name = $hash->{NAME}; # test api status my $param = { url => $AuthURL, timeout => 5, hash => $hash, method => "GET", header => "", callback => \&TadoAPI_callback }; #test if api is reachable HttpUtils_NonblockingGet($param); return undef; } sub TadoAPI_Connect(@) { my ($hash) = @_; my $name = $hash->{NAME}; my $tokenFileName = $tokenFile."_".$name; my $tokenLifeTime = $hash->{TOKEN_LIFETIME}; $tokenLifeTime = localtime($tokenLifeTime); #debug $debug = $attr{$name}{debug}; #load existing token, or try to refresh old one Log3 $name, 3, "TadoAPI $name" . ": " . "Loading Token Data from file: $tokenFileName." if $debug; TadoAPI_Status($hash); if($apiStatus){ eval { open(TOKENFILE, '<', $tokenFileName) or die("ERROR: $!"); $TokenData = decode_json()}; if($@ || $hash->{TOKEN_LIFETIME} < gettimeofday()){ Log3 $name, 3, "TadoAPI $name" . ": " . "Error while loading: $@" if $debug && $@; Log3 $name, 3, "TadoAPI $name" . ": " . "Token expired, requesting new one" if $debug && $hash->{TOKEN_LIFETIME} < gettimeofday(); TadoAPI_requestNewToken($hash); }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "Token expires at " . $tokenLifeTime if $debug; # if token is about to expire, refresh him if (($hash->{TOKEN_LIFETIME}-60) < gettimeofday()){ Log3 $name, 3, "TadoAPI $name" . ": " . "Token will expire soon, refreshing" if $debug; TadoAPI_refreshToken($hash); } } close(TOKENFILE); } return undef; } sub TadoAPI_requestNewToken(@) { my ($hash) = @_; my $name = $hash->{NAME}; my $username = $hash->{TADO_USER}; my $password = TadoAPI_readPassword($name); my $tokenFileName = $tokenFile."_".$name; Log3 $name, 3, "TadoAPI $name" . ": " . "Requesting new Token (TadoAPI_requestNewToken)" if $debug; $data = { client_id => $client_id, client_secret => $client_secret, username => $username, password => $password, scope => $scope, grant_type=>'password' }; $Request = POST($AuthURL,$data); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $Response = $UserAgent->request($Request); if($Response->is_success){ $TokenData = decode_json($Response->content); #write token data in file open(TOKENFILE,">$tokenFileName") or die("ERROR: $!"); print TOKENFILE $Response->content."\n"; close(TOKENFILE); # token time management $hash->{TOKEN_LIFETIME} = gettimeofday() + $TokenData->{'expires_in'}; Log3 $name, 3, "TadoAPI $name" . ": " . "Retrived new authentication token successfully. Valid until " . localtime($hash->{TOKEN_LIFETIME}) if $debug; #return to apistatus readingsBeginUpdate($hash); readingsBulkUpdate($hash, "API-Status", "reachable"); readingsEndUpdate( $hash, 1 ); return 1; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "Error in token retrival [Authentication Error]"; print $Response->status_line."\n" if $debug; #apiStatus down readingsBeginUpdate($hash); readingsBulkUpdate($hash, "API-Status", "error"); readingsEndUpdate( $hash, 1 ); return 0; } return undef; } sub TadoAPI_refreshToken(@) { my ($hash) = @_; my $name = $hash->{NAME}; my $tokenFileName = $tokenFile."_".$name; Log3 $name, 3, "TadoAPI $name" . ": " . "calling TadoAPI_refreshToken()" if $debug; $data = { client_id => $client_id, client_secret => $client_secret, scope => $scope, grant_type=>'refresh_token', refresh_token => $TokenData->{'refresh_token'} }; $Request = POST($AuthURL,$data); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 3,); $Response = $UserAgent->request($Request); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8"); if($Response->is_success){ Log3 $name, 3, "TadoAPI $name" . ": " . "refreshed authentication token successfully." if $debug; $TokenData = decode_json($Response->content); Log3 $name, 3, "TadoAPI $name" . ": " . "writting refreshed Token Data to file $tokenFileName" if $debug; open(TOKENFILE,">$tokenFileName") or die("ERROR: $!"); print TOKENFILE $Response->content."\n"; close(TOKENFILE); #Log3 $name, 3, "TadoAPI $name" . ": " . "refreshed Token successful written" if $debug; return 1; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "token expired, requesting new token"; Log3 $name, 3, "TadoAPI $name" . ": " . $Response->status_line if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "response code: " . $Response->code if $debug; #if token is expired and connection up, request new one if($Response->code == 500){ Log3 $name, 3, "TadoAPI $name" . ": " . "error: not connected - response: 500" if $debug; $apiStatus = 0; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "API-Status", "error"); readingsEndUpdate( $hash, 1 ); } else{ TadoAPI_requestNewToken($hash); } return 0; } return undef; } sub TadoAPI_Update(@){ my ($hash) = @_; my $name = $hash->{NAME}; Log3 $name, 3, "TadoAPI $name" . ": " . "TadoAPI_Update called" if $debug; # timer loop # my $nextTimer = "none"; # if api online, try again in 5 minutes if ( $apiStatus ) { $nextTimer = gettimeofday() + 300; } RemoveInternalTimer($hash); InternalTimer( $nextTimer, "TadoAPI_Update", $hash, 0 ); # update subs TadoAPI_GetUpdate($hash); return undef; } ######################## tado methods ######################## ############################################################## sub TadoAPI_GetUpdate(@){ my ($hash) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $mobileID = $attr{$name}{mobileID}; my $URL=$QueryURL.qq{/$homeID/zones/1/state}; my $deviceURL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; my $setting = 0; my $temperature = 0; my $humidity = 0; my $currentHeatingPower = 0; my $tadoMode = 0; my $success = 0; TadoAPI_Connect($hash); # zone specific updates if($apiStatus == 1){ $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = GET($URL); $Response = $UserAgent->request($Request); if($Response->is_success){ my $ResponseData = decode_json($Response->content); $temperature = $ResponseData->{'sensorDataPoints'}->{'insideTemperature'}->{'celsius'}; $tadoMode = $ResponseData->{'tadoMode'}; $humidity = $ResponseData->{'sensorDataPoints'}->{'humidity'}->{'percentage'}; $currentHeatingPower = $ResponseData->{'activityDataPoints'}->{'heatingPower'}->{'percentage'}; Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: Actual temperature is: $temperature Celsius and home mode $tadoMode" if $debug; $success = 1; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: [Authentication Error]". $Response->status_line; $success = 0; } # device specific updates $Request = GET($deviceURL); $Response = $UserAgent->request($Request); if($Response->is_success && $success){ my $ResponseData = decode_json($Response->content); $setting = $ResponseData->{'geoTrackingEnabled'}; Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: Actual geo setting for $mobileID is: $setting" if $debug; $success = 1; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: [Authentication Error]"; $success = 0; } if ($success){ # readings update readingsBeginUpdate($hash); readingsBulkUpdate($hash, "Temperatur", $temperature); readingsBulkUpdate($hash, "Geolocation", $setting); readingsBulkUpdate($hash, "HomeMode", $tadoMode); readingsBulkUpdate($hash, "Luftfeuchtigkeit", $humidity); readingsBulkUpdate($hash, "Heizleistung", $currentHeatingPower); readingsEndUpdate( $hash, 1 ); } $success = 0; } return undef; } sub TadoAPI_GetTemperature(@){ my ($hash) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $URL=$QueryURL.qq{/$homeID/zones/1/state}; my $temperature = 0; my $humidity = 0; Log3 $name, 3, "TadoAPI $name" . ": " . "homeID: $homeID" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "query-URL: $URL" if $debug; if($apiStatus == 1){ TadoAPI_Connect($hash); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = GET($URL); $Response = $UserAgent->request($Request); if($Response->is_success){ my $ResponseData = decode_json($Response->content); $temperature = $ResponseData->{'sensorDataPoints'}->{'insideTemperature'}->{'celsius'}; $humidity = $ResponseData->{'sensorDataPoints'}->{'humidity'}->{'percentage'}; Log3 $name, 3, "TadoAPI $name" . ": " . "Retriving zone 1 state:\n" . $Response->content if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Actual temperature is: $temperature Celsius" if $debug; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "Temperatur", $temperature); readingsBulkUpdate($hash, "Luftfeuchtigkeit", $humidity); readingsEndUpdate( $hash, 1 ); return $temperature; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; } } return undef; } sub TadoAPI_GetGeo(@){ my ($hash) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $mobileID = $attr{$name}{mobileID}; my $URL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; my $setting = 0; Log3 $name, 3, "TadoAPI $name" . ": " . "homeID: $homeID" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "mobID: $mobileID" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "query-URL: $URL" if $debug; if($apiStatus == 1){ TadoAPI_Connect($hash); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = GET($URL); $Response = $UserAgent->request($Request); if($Response->is_success){ my $ResponseData = decode_json($Response->content); $setting = $ResponseData->{'geoTrackingEnabled'}; #print "\n Retriving State:\n" if $debug; #print $Response->content."\n\n" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Actual geo setting for $mobileID is: $setting" if $debug; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "Geolocation", $setting); readingsEndUpdate( $hash, 1 ); return $setting; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error in TadoAPI_GetGeo()]"; Log3 $name, 3, "TadoAPI $name" . ": " . "Error: ". $Response->status_line if $debug; } } } sub TadoAPI_GetZoneInfo(@) { my ($hash) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $URL=$QueryURL.qq{/$homeID/zones/1/state}; if($apiStatus == 1){ TadoAPI_Connect($hash); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = GET($URL); $Response = $UserAgent->request($Request); if($Response->is_success){ my $ResponseData = decode_json($Response->content); Log3 $name, 3, "TadoAPI $name" . ": " . "Retriving zone 1 state:\n" . $Response->content if $debug; my $text = $Response->content; print "Retriving zone 1 state:\n" . $text; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; } } return undef; } sub TadoAPI_GetHomeMode(@) { my ($hash) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $URL=$QueryURL.qq{/$homeID/zones/1/state}; my $tadoMode = 0; if($apiStatus == 1){ TadoAPI_Connect($hash); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = GET($URL); $Response = $UserAgent->request($Request); if($Response->is_success){ my $ResponseData = decode_json($Response->content); $tadoMode = $ResponseData->{'tadoMode'}; #debug #{Log 3, ("Retriving Zone 1 State:") if $verbose}; #print $Response->content."\n\n" if $verbose; Log3 $name, 3, "TadoAPI $name" . ": " . "Actual tado mode is: $tadoMode" if $debug; readingsBeginUpdate($hash); readingsBulkUpdate($hash, "HomeMode", $tadoMode); readingsEndUpdate( $hash, 1 ); }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]: " . $Response->status_line if $debug; } } return 0; } sub TadoAPI_SetGeo(@){ my ($hash, $geo) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; my $mobileID = $attr{$name}{mobileID}; my $URL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; Log3 $name, 3, "TadoAPI $name" . ": " . "homeID: $homeID" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "geo: $geo" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "query-URL: $URL" if $debug; if($apiStatus == 1){ TadoAPI_Connect($hash); $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); $UserAgent->default_headers($header); $Request = HTTP::Request->new('PUT',$URL); $Request->content_type('application/json'); if($geo){ $Request->content('{"geoTrackingEnabled":"true"}'); }else{ $Request->content('{"geoTrackingEnabled":"false"}'); } $Response = $UserAgent->request($Request); if($Response->is_success){ print "\n Retriving State:\n" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Retriving state:\n" . $Response->content if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Set geo setting for $mobileID to: $geo"; }else{ Log3 $name, 3, "TadoAPI $name" . ": " . "Error in setGeo()"; Log3 $name, 3, "TadoAPI $name" . ": " . "Status: " . $Response->status_line if $debug; } } } 1; # Beginn der Commandref =pod =item [device] =item summary integration of the Tado API =item summary_DE Anbindung der Tado Heizungssteuerung über API =begin html - =end html =begin html_DE - =end html # Ende der Commandref =cut