2019-11-23 10:49:06 +00:00
#===============================================================================
2020-02-06 04:47:50 +00:00
# $Id: 98_TadoAPI.pm 102 2019-12-27 15:58:26Z psycho160 $
2019-11-23 10:49:06 +00:00
#
# FILE: 98_TadoAPI.pm
#
# USAGE: Module for FHEM
# Info: Turn verbose on for debugging
2020-04-21 22:40:51 +00:00
#
2019-11-23 10:49:06 +00:00
# REQUIREMENTS: Below modules should be pre-installed.
# HTTP::Request::Common
# HTTP::Headers
# Data::Dumper;
# JSON
#
# BUGS: ---
# NOTES: ---
# AUTHOR: Philipp Wolfmajer
# ORGANIZATION:
2019-12-10 09:14:23 +00:00
# VERSION: 0.6
# CREATED: 04/12/2019
# REVISION: 12/10/2019
2019-11-23 10:49:06 +00:00
#===============================================================================
package main ;
use strict ;
use warnings ;
use utf8 ;
use HTTP::Request::Common qw (POST GET PUT ) ;
use HTTP::Headers ;
use JSON ;
2019-11-25 21:02:47 +00:00
use POSIX qw( strftime ) ;
2019-11-23 10:49:06 +00:00
####DEFAULTS############
2020-04-21 22:40:51 +00:00
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 $ header = { } ;
my $ reqDebug = 5 ;
2019-11-23 10:49:06 +00:00
# helpers
my $ apiStatus = 1 ;
2020-04-19 22:36:32 +00:00
my % sets = (
2020-04-21 22:40:51 +00:00
"zoneUpdate" = > "" ,
"refreshToken" = > "noArg" ,
"password" = > "" ,
"update" = > "noArg" ,
"setGeo" = > "" ,
"setZoneOverlay" = > "" ,
"timedZoneOverlay" = > "" ,
"updateAllOverlays" = > "noArg" ,
"setAllOverlays" = > ""
) ;
2019-11-23 10:49:06 +00:00
2020-04-19 22:36:32 +00:00
my % gets = (
2020-04-21 22:40:51 +00:00
"getZoneDevices" = > "noArg" ,
"getZoneInfo" = > "noArg" ,
"getGeo" = > "" ,
#"getXTest" => "",
"getMobileDevices" = > "noArg"
2019-11-23 10:49:06 +00:00
) ;
2020-04-21 22:40:51 +00:00
sub TadoAPI_Initialize {
my $ hash = shift ;
my $ TYPE = "TadoAPI" ;
$ hash - > { DefFn } = \ & TadoAPI_Define ;
$ hash - > { InitFn } = \ & TadoAPI_Init ;
$ hash - > { SetFn } = \ & TadoAPI_Set ;
$ hash - > { GetFn } = \ & TadoAPI_Get ;
$ hash - > { AttrList } = ""
. "homeID "
. "mobileID "
. "showPosData:0,1 "
. "updateIntervall "
. $ readingFnAttributes ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_Init {
my $ hash = shift ;
my @ args = @ _ ;
2020-04-20 14:13:19 +00:00
2020-04-21 22:40:51 +00:00
my $ u =
"wrong syntax: define <name> TadoAPI <username> <homeID> [<mobileID>]" ;
return $ u if ( int ( @ args ) < 2 ) ;
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_Define {
my $ hash = shift ;
my $ def = shift ;
2020-04-20 14:13:19 +00:00
2020-04-21 22:40:51 +00:00
my @ a = split ( "[ \t]+" , $ def ) ;
my $ name = shift @ a ;
my $ type = shift @ a ;
my $ tokenFileName = $ tokenFile . "_" . $ name ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
return "Invalid number of arguments: "
2019-11-23 10:49:06 +00:00
. "define <name> TadoAPI <username> [<homeID>]"
2020-04-21 22:40:51 +00:00
if ( int ( @ a ) < 1 ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my ( $ user , $ homeID ) = @ a ;
Log3 $ name , 3 , "TadoAPI_Define $name: called " ;
$ hash - > { STATE } = "defined" ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
# Initialize the device
return $@ unless ( FHEM::Meta:: SetInternals ( $ hash ) ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
$ hash - > { TADO_USER } = $ user ;
$ hash - > { TOKEN_FILE } = $ tokenFileName ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my @ args = ( $ homeID ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( $ main:: init_done ) {
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
# do something?
return TadoAPI_Catch ( $@ ) if $@ ;
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ password = TadoAPI_readPassword ( $ name ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( defined ( $ password ) ) {
TadoAPI_CheckStatus ( $ hash ) ;
TadoAPI_LoadToken ( $ hash ) ;
# start the status update timer
RemoveInternalTimer ( $ hash ) ;
InternalTimer ( gettimeofday ( ) + 15 , "TadoAPI_Update" , $ hash , 0 ) ;
if ( defined ( $ homeID ) && $ homeID ne "" ) {
$ attr { $ name } { homeID } = $ homeID ;
}
else {
my $ id = TadoAPI_GetHomeId ( $ hash ) ;
if ( defined ( $ id ) && $ id ne "" ) {
$ attr { $ name } { homeID } = $ id ;
}
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
else {
$ hash - > { STATE } = "no password set" ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_Set {
my $ hash = shift ;
my @ a = @ _ ;
return "Need at least one parameters" if ( @ a < 2 ) ;
my $ cmd = $ a [ 1 ] ;
my $ value = $ a [ 2 ] ;
my $ name = $ hash - > { NAME } ;
my $ subcmd ;
my $ message = undef ;
if ( ! defined ( $ sets { $ cmd } ) ) {
my @ cmds = ( ) ;
for my $ key ( sort keys % sets ) {
push @ cmds ,
$ sets { $ key } ? $ key . ":" . join ( "," , $ sets { $ key } ) : $ key ;
}
return "Unknown argument $a[1], choose one of " . join ( " " , @ cmds ) ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
if ( ( $ cmd ne "password" ) ) {
my $ pwd = TadoAPI_readPassword ( $ name ) ;
unless ( defined $ pwd ) {
$ message =
"Error: no tado password set. Please define it with 'set $name password Your_tado_Password'" ;
Log3 $ name , 2 , "$name, $message" ;
$ hash - > { STATE } = "no password set" ;
return $ message ;
}
2019-11-23 19:22:41 +00:00
}
2020-04-21 22:40:51 +00:00
if ( $ cmd eq 'setGeo' ) {
return "Need at least two parameters (mobileID, Setting)" if ( @ a < 4 ) ;
if ( $ a [ 3 ] eq "on" ) {
Log3 $ name , 3 , "TadoAPI: set $name: processing ($cmd)" ;
TadoAPI_SetGeoById ( $ hash , $ value , 1 ) ;
}
else {
Log3 $ name , 3 , "TadoAPI: set $name: processing ($cmd)" ;
TadoAPI_SetGeoById ( $ hash , $ value , 0 ) ;
}
TadoAPI_GetGeoById ( $ hash , $ value ) ;
}
elsif ( $ cmd eq 'setZoneOverlay' ) {
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
return
"Need at least two parameters [ZoneID] [Setting] (duration in sec); Setting Info: remove=delete overlay; 0=heating power off; 1<=desired temperature (overlay)"
if ( @ a < 4 ) ;
if ( $ a [ 3 ] eq "remove" ) {
TadoAPI_SetZoneOverlayById ( $ hash , $ value , "remove" ) ;
}
elsif ( defined ( $ a [ 4 ] ) ) {
TadoAPI_SetZoneOverlayById ( $ hash , $ value , $ a [ 3 ] , $ a [ 4 ] ) ;
}
elsif ( $ a [ 3 ] >= 0 ) {
TadoAPI_SetZoneOverlayById ( $ hash , $ value , $ a [ 3 ] ) ;
}
Log3 $ name , 4 , "TadoAPI $name" . ": " . "$cmd finished" ;
}
elsif ( $ cmd eq 'timedZoneOverlay' ) {
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
return
"Need at least three parameters [ZoneID] [Duration (sec)] [Setting]"
if ( @ a < 4 ) ;
if ( defined ( $ a [ 4 ] ) ) {
TadoAPI_SetTimedZoneOverlay ( $ hash , $ value , $ a [ 3 ] , $ a [ 4 ] ) ;
}
Log3 $ name , 4 , "TadoAPI $name" . ": " . "$cmd finished" ;
}
elsif ( $ cmd eq 'setAllOverlays' ) {
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
return
"Need at least one parameter (Setting) - Setting: remove=delete overlay; 0=heating power off; 1<=desired temperature (overlay)"
if ( @ a < 3 ) ;
if ( $ value eq "remove" ) {
TadoAPI_SetAllOverlays ( $ hash , "remove" ) ;
}
elsif ( $ value >= 0 ) {
TadoAPI_SetAllOverlays ( $ hash , $ value ) ;
}
Log3 $ name , 4 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
}
elsif ( $ cmd eq 'updateAllOverlays' ) {
2019-12-14 15:29:46 +00:00
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
TadoAPI_GetAllZoneOverlays ( $ hash ) ;
Log3 $ name , 4 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
2020-04-21 22:40:51 +00:00
}
elsif ( $ cmd eq 'refreshToken' ) {
2019-11-23 10:49:06 +00:00
Log3 $ name , 3 , "TadoAPI: set $name: processing ($cmd)" ;
RemoveInternalTimer ( $ hash ) ;
InternalTimer ( gettimeofday ( ) + 10 , "TadoAPI_Update" , $ hash , 0 ) ;
TadoAPI_LoadToken ( $ hash ) ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
2020-04-21 22:40:51 +00:00
}
elsif ( $ cmd eq 'update' ) {
2019-11-23 10:49:06 +00:00
Log3 $ name , 3 , "TadoAPI: set $name: processing ($cmd)" ;
2019-12-10 12:21:10 +00:00
TadoAPI_UpdateFn ( $ hash ) ;
2019-11-23 10:49:06 +00:00
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
2020-04-21 22:40:51 +00:00
}
elsif ( $ cmd eq 'zoneUpdate' ) {
2019-11-23 10:49:06 +00:00
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
2020-04-21 22:40:51 +00:00
return "ZoneID as parameter needed" if ( ! $ value ) ;
if ( $ value >= 1 ) {
my ( $ temperature , $ humidity , $ desiredTemp , $ currentHeatingPower ,
$ overlay )
= TadoAPI_GetZoneReadingsById ( $ hash , $ value ) ;
my $ zoneName = TadoAPI_GetZoneNameById ( $ hash , $ value ) ;
if ( defined ( $ zoneName ) ) {
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "Temperatur_" . $ zoneName ,
$ temperature ) ;
readingsBulkUpdate ( $ hash , "Luftfeuchtigkeit_" . $ zoneName ,
$ humidity ) ;
readingsBulkUpdate ( $ hash , "Heizleistung_" . $ zoneName ,
$ currentHeatingPower ) ;
readingsBulkUpdate ( $ hash , "OverlayType_" . $ zoneName ,
$ overlay ) ;
readingsBulkUpdate ( $ hash , "DesiredTemp_" . $ zoneName ,
$ desiredTemp ) ;
readingsEndUpdate ( $ hash , 1 ) ;
$ message = "OK" ;
}
}
else {
return "Wrong ZoneID" ;
}
2019-11-23 10:49:06 +00:00
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
2020-04-21 22:40:51 +00:00
}
elsif ( $ cmd eq 'password' ) {
2019-11-23 10:49:06 +00:00
Log3 $ name , 3 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
2020-04-21 22:40:51 +00:00
2019-11-23 10:49:06 +00:00
# name und cmd überspringen
shift @ a ;
shift @ a ;
2020-04-21 22:40:51 +00:00
2019-11-23 10:49:06 +00:00
# den Rest der das passwort enthält, als ein String
2020-04-21 22:40:51 +00:00
$ subcmd = join ( " " , @ a ) ;
$ message = TadoAPI_storePassword ( $ name , $ subcmd ) ;
2020-04-19 22:36:32 +00:00
2019-11-23 10:49:06 +00:00
# start the status update timer
RemoveInternalTimer ( $ hash ) ;
InternalTimer ( gettimeofday ( ) + 10 , "TadoAPI_Update" , $ hash , 0 ) ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
2020-04-21 22:40:51 +00:00
}
2019-11-23 10:49:06 +00:00
return $ message if $ message ;
return TadoAPI_Catch ( $@ ) if $@ ;
2020-04-19 20:13:20 +00:00
return ;
2020-04-21 22:40:51 +00:00
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
sub TadoAPI_Get {
my $ hash = shift ;
my @ 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 $ message = undef ;
if ( ! defined ( $ gets { $ cmd } ) ) {
my @ cmds = ( ) ;
for my $ key ( sort keys % gets ) {
push @ cmds ,
$ gets { $ key } ? $ key . ":" . join ( "," , $ gets { $ key } ) : $ key ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return "Unknown argument $a[1], choose one of " . join ( " " , @ cmds ) ;
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ pwd = TadoAPI_readPassword ( $ name ) ;
unless ( defined $ pwd ) {
$ message =
"Error: no tado password set. Please define it with 'set $name password Your_tado_Password'" ;
Log3 $ name , 2 , "$name, $message" ;
$ hash - > { STATE } = "no password set" ;
return $ message ;
}
if ( $ cmd =~ /\Qget\E/ ) {
COMMAND_HANDLER: {
$ cmd eq "getGeo" and do {
return "Need at least one parameter (mobileID)" if ( @ a < 3 ) ;
return "Wrong MobileID" if ( length ( $ value ) < 6 ) ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
TadoAPI_GetGeoById ( $ hash , $ value ) ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
last ;
} ;
$ cmd eq "getMobileDevices" and do {
Log3 $ name , 3 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
my @ data = TadoAPI_GetMobileDevices ( $ hash ) ;
$ message = "Device List:\n" ;
for my $ item ( @ data ) {
$ message . = $ item - > { 'name' } . ": " . $ item - > { 'id' } . "\n" ;
}
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished" ;
last ;
} ;
# only for testing
$ cmd eq "getXTest" and do {
Log3 $ name , 5 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
my $ zoneName = TadoAPI_GetZoneNameById ( $ hash , $ value ) ;
$ zoneName = "wrong Zone ID" unless $ zoneName ;
$ message = "Name: " . $ zoneName ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
last ;
} ;
$ cmd eq "getZoneDevices" and do {
Log3 $ name , 3 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
my @ devArr = TadoAPI_GetTadoDevices ( $ hash ) ;
my $ devicecount = 0 ;
$ message = "Tado-Device(s):\n" ;
for ( my $ i = 0 ; $ i < @ devArr ; $ i + + ) {
my $ tadodevices = $ devArr [ $ i ] - > { 'devices' } ;
$ message . = "ZoneID: " . ( $ devArr [ $ i ] - > { 'id' } ) ;
my $ spacer = 0 ;
for my $ item ( @$ tadodevices ) {
$ message . = "\t " if ( $ spacer > 0 ) ;
$ message . = " "
. $ item - > { 'serialNo' }
. " Battery: "
. $ item - > { 'batteryState' } . "\n" ;
$ devicecount + + ;
$ spacer + + ;
}
}
$ message . =
"There are $devicecount Tado-Device(s) in this HomeID("
. $ attr { $ name } { homeID } . ")." ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
last ;
} ;
$ cmd eq "getZoneInfo" and do {
Log3 $ name , 3 , "TadoAPI $name" . ": " . "processing ($cmd)" ;
TadoAPI_GetZoneInfo ( $ hash ) ;
my $ zonecount = TadoAPI_GetZoneCount ( $ hash ) ;
$ message = "You have $zonecount Zones in Home "
. $ attr { $ name } { homeID } . ".\n" ;
my @ devArr = TadoAPI_GetTadoDevices ( $ hash ) ;
for ( my $ i = 0 ; $ i < @ devArr ; $ i + + ) {
my $ zoneID = $ devArr [ $ i ] - > { 'id' } ;
$ message . = "Zone ID $zoneID: "
. TadoAPI_GetZoneNameById ( $ hash , $ zoneID ) . "\n" ;
}
$ message . = "See Logfile for more Info" ;
Log3 $ name , 3 , "TadoAPI $name" . ": " . "$cmd finished\n" ;
last ;
} ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return $ message if $ message ;
return TadoAPI_Catch ( $@ ) if $@ ;
return ;
2019-11-23 10:49:06 +00:00
}
}
2020-04-19 21:44:16 +00:00
sub TadoAPI_Catch {
2020-04-21 22:40:51 +00:00
my $ exception = shift ;
if ( $ exception ) {
$ exception =~ /^(.*)( at.*FHEM.*)$/ ;
return $ 1 ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_Undefine {
my $ hash = shift ;
my $ name = shift ;
2019-11-23 10:49:06 +00:00
RemoveInternalTimer ( $ hash ) ;
2020-04-21 22:40:51 +00:00
2019-11-23 10:49:06 +00:00
#todo remove tokenfile
2020-04-19 20:13:20 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_CheckStatus {
my $ hash = shift ;
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
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $AuthURL" ;
HttpUtils_NonblockingGet ( $ param ) ;
return ;
2019-12-10 14:28:32 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_LoadToken {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ tokenFileName = $ tokenFile . "_" . $ name ;
my $ tokenLifeTime = $ hash - > { TOKEN_LIFETIME } ;
$ tokenLifeTime = 0 if ( ! defined $ tokenLifeTime || $ tokenLifeTime eq '' ) ;
my $ Token = undef ;
if ( $ apiStatus ) {
open ( my $ TOKENFILE , q{ < } , $ tokenFileName ) or croak ( "ERROR: $!" ) ;
eval { $ Token = decode_json ( <$TOKENFILE> ) } ;
close ( $ TOKENFILE ) ;
if ( $@ || $ tokenLifeTime < gettimeofday ( ) ) {
Log3 $ name , 5 ,
"TadoAPI $name" . ": "
. "Error while loading: $@ ,requesting new one"
if $@ ;
Log3 $ name , 5 ,
"TadoAPI $name" . ": " . "Token is expired, requesting new one"
if $ tokenLifeTime < gettimeofday ( ) ;
$ Token = TadoAPI_NewTokenRequest ( $ hash ) ;
TadoAPI_CheckStatus ( $ hash ) ;
}
else {
Log3 $ name , 5 ,
"TadoAPI $name" . ": "
. "Token expires at "
. localtime ( $ tokenLifeTime ) ;
# if token is about to expire, refresh him
if ( ( $ tokenLifeTime - 45 ) < gettimeofday ( ) ) {
Log3 $ name , 5 ,
"TadoAPI $name" . ": " . "Token will expire soon, refreshing" ;
$ Token = TadoAPI_TokenRefresh ( $ hash ) ;
}
}
return $ Token if $ Token ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
TadoAPI_CheckStatus ( $ hash ) ;
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_NewTokenRequest {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ username = $ hash - > { TADO_USER } ;
my $ password = TadoAPI_readPassword ( $ name ) ;
my $ tokenFileName = $ tokenFile . "_" . $ name ;
Log3 $ name , 5 , "TadoAPI $name" . ": " . "calling NewTokenRequest()" ;
my $ data = {
client_id = > $ client_id ,
client_secret = > $ client_secret ,
username = > $ username ,
password = > $ password ,
scope = > $ scope ,
grant_type = > 'password'
} ;
my $ param = {
url = > $ AuthURL ,
method = > 'POST' ,
timeout = > 5 ,
hash = > $ hash ,
data = > $ data
} ;
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $AuthURL" ;
my ( $ err , $ returnData ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "NewTokenRequest: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ returnData ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $returnData" ;
my $ decoded_data = eval { decode_json ( $ returnData ) } ;
if ( $@ ) {
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "NewTokenRequest: decode_json failed, invalid json. error: $@ " ;
}
else {
#write token data in file
open ( my $ TOKENFILE , q{ > } , $ tokenFileName ) or croak ( "ERROR: $!" ) ;
print $ TOKENFILE $ returnData . "\n" ;
close ( $ TOKENFILE ) ;
# token lifetime management
$ hash - > { TOKEN_LIFETIME } =
gettimeofday ( ) + $ decoded_data - > { 'expires_in' } ;
$ hash - > { TOKEN_LIFETIME_HR } = localtime ( $ hash - > { TOKEN_LIFETIME } ) ;
Log3 $ name , 5 ,
"TadoAPI $name" . ": "
. "Retrived new authentication token successfully. Valid until "
. localtime ( $ hash - > { TOKEN_LIFETIME } ) ;
$ hash - > { STATE } = "reachable" ;
return $ decoded_data ;
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_TokenRefresh {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ tokenFileName = $ tokenFile . "_" . $ name ;
my $ Token = undef ;
# load token
open ( my $ TOKENFILE , q{ < } , $ tokenFileName ) or croak ( "ERROR: $!" ) ;
eval { $ Token = decode_json ( <$TOKENFILE> ) } ;
close ( $ TOKENFILE ) ;
my $ data = {
client_id = > $ client_id ,
client_secret = > $ client_secret ,
scope = > $ scope ,
grant_type = > 'refresh_token' ,
refresh_token = > $ Token - > { 'refresh_token' }
} ;
my $ param = {
url = > $ AuthURL ,
method = > 'POST' ,
timeout = > 5 ,
hash = > $ hash ,
data = > $ data
} ;
#Log3 $name, 5, 'Blocking GET TokenRefresh: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $AuthURL" ;
my ( $ err , $ returnData ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "TokenRefresh: Error in token retrival while requesting "
. $ param - > { url }
. " - $err" ;
$ hash - > { STATE } = "error" ;
}
elsif ( $ returnData ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $returnData" ;
my $ decoded_data = eval { decode_json ( $ returnData ) ; } ;
if ( $@ ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "TokenRefresh: decode_json failed, invalid json. error:$@\n"
if $@ ;
$ hash - > { STATE } = "error" ;
}
else {
#write token data in file
open ( my $ TOKENFILE , q{ > } , $ tokenFileName ) or croak ( "ERROR: $!" ) ;
print $ TOKENFILE $ returnData . "\n" ;
close ( $ TOKENFILE ) ;
# token lifetime management
$ hash - > { TOKEN_LIFETIME } =
gettimeofday ( ) + $ decoded_data - > { 'expires_in' } ;
$ hash - > { TOKEN_LIFETIME_HR } = localtime ( $ hash - > { TOKEN_LIFETIME } ) ;
Log3 $ name , 5 ,
"TadoAPI $name" . ": "
. "TokenRefresh: Refreshed authentication token successfully. Valid until "
. localtime ( $ hash - > { TOKEN_LIFETIME } ) ;
$ hash - > { STATE } = "reachable" ;
return $ decoded_data ;
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_Update {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
Log3 $ name , 5 , "TadoAPI $name" . ": " . "TadoAPI_Update called" ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ nextTimer = "none" ;
my $ intervall = 300 ;
2019-12-10 14:28:32 +00:00
2020-04-21 22:40:51 +00:00
$ intervall = $ attr { $ name } { updateIntervall }
if ( defined ( $ attr { $ name } { updateIntervall } )
&& $ attr { $ name } { updateIntervall } =~ m/^-?\d+$/ ) ;
$ nextTimer = gettimeofday ( ) + $ intervall ;
$ hash - > { NEXT_UPDATE } = localtime ( $ nextTimer ) ;
2019-12-10 14:28:32 +00:00
2020-04-21 22:40:51 +00:00
Log3 $ name , 5 , "TadoAPI $name" . ": " . "Next Timer = $nextTimer" ;
2019-12-10 14:28:32 +00:00
2020-04-21 22:40:51 +00:00
RemoveInternalTimer ( $ hash ) ;
InternalTimer ( $ nextTimer , "TadoAPI_Update" , $ hash , 0 ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
# update subs
TadoAPI_UpdateFn ( $ hash ) ;
return ;
2019-11-23 10:49:06 +00:00
}
######################## tado methods ########################
##############################################################
2020-04-21 22:40:51 +00:00
sub TadoAPI_SetZoneOverlayById {
my $ hash = shift ;
my $ zoneID = shift ;
my $ setting = shift ;
my $ duration = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/zones/$zoneID/overlay } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( defined ( $ CurrentTokenData ) ) {
my $ method = "" ;
my $ myjson = undef ;
2020-04-19 22:36:32 +00:00
2020-04-21 22:40:51 +00:00
Log3 $ name , 5 ,
"TadoAPI $name"
. ": SetOverlay for Zone $zoneID (Setting: "
. $ setting . ") - "
. "query-URL: $URL" ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ dt = time ( ) ;
$ dt += $ duration if defined ( $ duration ) ;
2019-11-25 21:02:47 +00:00
2020-04-21 22:40:51 +00:00
delete ( $ hash - > { helper } { LockedZones } { $ zoneID } ) ;
2020-02-07 13:26:15 +00:00
2020-04-21 22:40:51 +00:00
# remove overlay
if ( $ setting eq "remove" ) {
$ method = "DELETE" ;
Log3 $ name , 3 ,
"TadoAPI $name" . ": " . "Deleting Overlay for Zone $zoneID" ;
}
# turn heating of
elsif ( $ setting == 0 ) {
# turn off for timer
if ( defined ( $ duration ) && $ duration > 0 ) {
$ method = "PUT" ;
$ myjson = {
type = > "MANUAL" ,
setting = > {
type = > "HEATING" ,
power = > "OFF"
} ,
termination = > {
type = > "TIMER" ,
durationInSeconds = > $ duration ,
expiry = > strftime ( '%Y-%m-%dT%H:%M:%SZ' , gmtime ( $ dt ) )
}
} ;
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "Timer Overlay for Zone $zoneID . Power off for: $duration seconds" ;
2019-11-25 21:02:47 +00:00
}
2020-04-21 22:40:51 +00:00
# infinite off
else {
$ method = "PUT" ;
$ myjson = {
setting = > {
type = > "HEATING" ,
power = > "OFF" ,
} ,
termination = > {
type = > "MANUAL"
} ,
} ;
}
}
elsif ( $ setting > 0 ) {
# set timed overlay
if ( defined ( $ duration ) && $ duration > 0 ) {
$ method = "PUT" ;
$ myjson = {
type = > "MANUAL" ,
setting = > {
type = > "HEATING" ,
power = > "ON" ,
temperature = > {
celsius = > $ setting
}
} ,
termination = > {
type = > "TIMER" ,
durationInSeconds = > $ duration ,
expiry = > strftime ( '%Y-%m-%dT%H:%M:%SZ' , gmtime ( $ dt ) ) ,
}
} ;
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "Set Timer Overlay for Zone $zoneID with $duration seconds expire." ;
# seet lock for this zone
$ hash - > { helper } { LockedZones } { $ zoneID } = "locked" ;
}
else {
# infinite setting
$ method = "PUT" ;
$ myjson = {
setting = > {
type = > "HEATING" ,
power = > "ON" ,
temperature = > {
celsius = > $ setting
} ,
} ,
termination = > {
type = > "MANUAL"
} ,
} ;
}
}
$ myjson = encode_json ( $ myjson ) if ( defined ( $ myjson ) ) ;
my $ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = >
"$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
2019-11-23 10:49:06 +00:00
} ,
2020-04-21 22:40:51 +00:00
method = > $ method ,
timeout = > 5 ,
callback = > \ & Tado_UpdateZoneOverlayCallback ,
hash = > $ hash ,
setting = > $ setting ,
zoneID = > $ zoneID ,
data = > $ myjson
2019-11-23 10:49:06 +00:00
} ;
2020-04-21 22:40:51 +00:00
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_SetAllOverlays {
my $ hash = shift ;
my $ setting = shift ;
my $ duration = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my @ zones = TadoAPI_GetTadoDevices ( $ hash ) ;
for ( my $ i = 0 ; $ i < @ zones ; $ i + + ) {
my $ zoneID = $ zones [ $ i ] - > { 'id' } ;
if ( defined ( $ duration ) && $ duration > 0 ) {
TadoAPI_SetZoneOverlayById ( $ hash , $ zoneID , $ setting , $ duration ) ;
}
else {
TadoAPI_SetZoneOverlayById ( $ hash , $ zoneID , $ setting ) ;
}
2020-02-06 04:47:50 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-12-14 15:29:46 +00:00
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetAllZoneOverlays {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my @ zones = TadoAPI_GetTadoDevices ( $ hash ) ;
for my $ zone ( @ zones ) {
my $ zoneID = $ zone - > { 'id' } ;
my $ zoneName = TadoAPI_ReplaceUmlaute ( $ zone - > { 'name' } ) ;
my ( $ temperature , $ humidity , $ desiredTemp , $ currentHeatingPower ,
$ overlay )
= TadoAPI_GetZoneReadingsById ( $ hash , $ zoneID ) ;
readingsSingleUpdate ( $ hash , "OverlayType_" . $ zoneName , $ overlay , 1 ) ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_UpdateFn {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
my $ homeID = $ attr { $ name } { homeID } ;
if ( $ apiStatus == 1 && defined ( $ CurrentTokenData ) ) {
# zone specific updates
my $ URL = $ QueryURL . qq{ /$homeID/zones } ;
my $ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = >
"$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 25 ,
incrementalTimout = > 1 ,
hash = > $ hash ,
callback = > \ & TadoAPI_UpdateAllZoneReadingsCallback
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "UpdFN: Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
# mobile devices
$ URL = $ QueryURL . qq{ /$homeID/mobileDevices } ;
$ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = >
"$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 7 ,
incrementalTimout = > 1 ,
callback = > \ & TadoAPI_UpdateMobileReadingsCallback ,
hash = > $ hash
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
}
return ;
2019-11-23 10:49:06 +00:00
}
########################################################################################################################################################################
# Callback Subs
########################################################################################################################################################################
2020-04-21 22:40:51 +00:00
sub TadoAPI_callback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
$ param - > { code } = 0 unless defined $ param - > { code } ;
if ( $ param - > { code } == 401 || $ param - > { code } == 400 ) {
$ apiStatus = 1 ;
$ hash - > { STATE } = "reachable" ;
Log3 $ name , 5 ,
"TadoAPI $name" . ": "
. "API is reachable. Callback Status: "
. $ param - > { code } ;
}
else {
$ apiStatus = 0 ;
$ hash - > { STATE } = "error" ;
Log3 $ name , 3 ,
"TadoAPI $name" . ": " . "API error: apiStatus $apiStatus ($err)" ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_UpdateAllZoneReadingsCallback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
2019-12-10 16:41:08 +00:00
2020-04-21 22:40:51 +00:00
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"Error in TadoAPI_UpdateZoneCallback while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
# if api returns error
eval { my $ error = @$ decoded_data ; } ;
if ( $@ ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "UpdateAllZonesCallback: decode_json failed, invalid json. error:$@\n"
if $@ ;
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "UpdateAllZonesCallback: Error in decoded data, Code: "
. $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' }
if ( exists ( $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' } ) ) ;
$ hash - > { LastRequest } = "error" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
else {
readingsBeginUpdate ( $ hash ) ;
for my $ zone ( @$ decoded_data ) {
my $ zoneID = $ zone - > { 'id' } ;
my $ zoneName = TadoAPI_ReplaceUmlaute ( $ zone - > { 'name' } ) ;
Log3 $ name , 5 , "TadoAPI $name" . ": "
. "Set Reading Update for Zone $zoneID " ;
my ( $ temperature , $ humidity , $ desiredTemp ,
$ currentHeatingPower , $ overlay )
= TadoAPI_GetZoneReadingsById ( $ hash , $ zoneID ) ;
# updates zone readings
readingsBulkUpdate ( $ hash , "Temperatur_" . $ zoneName ,
$ temperature ) ;
readingsBulkUpdate ( $ hash , "Luftfeuchtigkeit_" . $ zoneName ,
$ humidity ) ;
readingsBulkUpdate ( $ hash , "Heizleistung_" . $ zoneName ,
$ currentHeatingPower ) ;
readingsBulkUpdate ( $ hash , "OverlayType_" . $ zoneName ,
$ overlay ) ;
readingsBulkUpdate ( $ hash , "DesiredTemp_" . $ zoneName ,
$ desiredTemp ) ;
# iterate through all devices in zone
my $ devices = $ zone - > { 'devices' } ;
for my $ device ( @$ devices ) {
readingsBulkUpdate (
$ hash ,
"Battery_" . $ device - > { 'serialNo' } ,
$ device - > { 'batteryState' }
) ;
}
}
readingsEndUpdate ( $ hash , 1 ) ;
}
my $ zonecount = TadoAPI_GetZoneCount ( $ hash ) ;
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , "ActiveZones" , $ zonecount ) ;
readingsEndUpdate ( $ hash , 0 ) ;
$ hash - > { LastRequest } = "OK" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub Tado_UpdateZoneOverlayCallback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
my $ zoneID = $ param - > { zoneID } ;
my $ setting = $ param - > { setting } ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"Error in UpdateZoneOverlayCallback while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "set (async) Overlay for Zone $zoneID to: $setting" ;
2020-04-19 22:36:32 +00:00
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
# finaly update readings
my ( $ temperature , $ humidity , $ desiredTemp , $ currentHeatingPower , $ overlay )
= TadoAPI_GetZoneReadingsById ( $ hash , $ zoneID ) ;
my $ zoneName = TadoAPI_GetZoneNameById ( $ hash , $ zoneID ) ;
if ( defined ( $ zoneName ) ) {
# updates zone readings
readingsBeginUpdate ( $ hash ) ;
# readingsBulkUpdate($hash, "Temperatur_" . $zoneName, $temperature);
# readingsBulkUpdate($hash, "Luftfeuchtigkeit_" . $zoneName, $humidity);
# readingsBulkUpdate($hash, "Heizleistung_" . $zoneName, $currentHeatingPower);
readingsBulkUpdate ( $ hash , "OverlayType_" . $ zoneName , $ overlay ) ;
readingsBulkUpdate ( $ hash , "DesiredTemp_" . $ zoneName , $ desiredTemp ) ;
# lock zone if timed overlay
if ( exists ( $ hash - > { helper } - > { LockedZones } { $ zoneID } ) ) {
readingsBulkUpdate ( $ hash , "Zone" . $ zoneID . "Lock" , "timer" ) ;
readingsEndUpdate ( $ hash , 1 ) ;
}
else {
readingsEndUpdate ( $ hash , 1 ) ;
readingsDelete ( $ hash , "Zone" . $ zoneID . "Lock" ) ;
}
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_LogInfoCallback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"Error in LogInfoCallback while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": " . $ param - > { infotext } . ":\n" . $ data . "\n" ;
}
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_SetGeoByIdCallback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"Error in TadoAPI_SetGeoByIdCallback while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 3 , "SetGeoById URL: " . $ param - > { url } . " returned: $data" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_UpdateMobileReadingsCallback {
my $ param = shift ;
my $ err = shift ;
my $ data = shift ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"Error in UpdateMobileReadingsCallback while requesting "
. $ param - > { url }
. " - $err" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
# if api returns error
eval { my $ error = @$ decoded_data ; } ;
if ( $@ ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "Decode_json failed, invalid json. error:$@\n"
if $@ ;
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "Error in UpdateMobileReadingsCallback, Code: "
. $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' } ;
$ hash - > { LastRequest } = "error" ;
}
else {
for my $ item ( @$ decoded_data ) {
TadoAPI_GetGeoById ( $ hash , $ item - > { 'id' } , $ item ) ;
}
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetZoneInfo {
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
if ( defined ( $ CurrentTokenData ) ) {
# HomeInfo
my $ URL = qq{ https://my.tado.com/api/v2/me } ;
my $ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 8 ,
infotext = > "HomeInfos" ,
hash = > $ hash ,
callback = > \ & TadoAPI_LogInfoCallback
} ;
HttpUtils_NonblockingGet ( $ request ) ;
# TadoDevicesInfo
$ URL = $ QueryURL . qq{ /$homeID/zones } ;
$ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 3 ,
infotext = > "Tado Devices Info" ,
hash = > $ hash ,
callback = > \ & TadoAPI_LogInfoCallback
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
# Mobileinfo
$ URL = $ QueryURL . qq{ /$homeID/mobileDevices } ;
$ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 3 ,
infotext = > "Mobile Devices Info" ,
hash = > $ hash ,
callback = > \ & TadoAPI_LogInfoCallback
} ;
HttpUtils_NonblockingGet ( $ request ) ;
my @ mobDev = TadoAPI_GetMobileDevices ( $ hash ) ;
for ( my $ i = 0 ; $ i < @ mobDev ; $ i + + ) {
my $ mobileID = $ mobDev [ $ i ] - > { 'id' } ;
$ URL = $ QueryURL . qq{ /$homeID/mobileDevices/$mobileID/settings } ;
$ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 3 ,
infotext = > "Mobile Device $mobileID" ,
hash = > $ hash ,
callback = > \ & TadoAPI_LogInfoCallback
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
}
# zones
my @ devArr = TadoAPI_GetTadoDevices ( $ hash ) ;
for ( my $ i = 0 ; $ i < @ devArr ; $ i + + ) {
my $ zoneID = $ devArr [ $ i ] - > { 'id' } ;
$ URL = $ QueryURL . qq{ /$homeID/zones/$zoneID/state } ;
my $ infotext =
"ZoneID $zoneID ("
. TadoAPI_GetZoneNameById ( $ hash , $ zoneID )
. ") Status" ;
$ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 3 ,
infotext = > $ infotext ,
hash = > $ hash ,
callback = > \ & TadoAPI_LogInfoCallback
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
HttpUtils_NonblockingGet ( $ request ) ;
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
}
sub TadoAPI_SetGeoById {
my $ hash = shift ;
my $ mobileID = shift ;
my $ geo = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/mobileDevices/$mobileID/settings } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
my $ data = { } ;
if ( defined ( $ CurrentTokenData ) ) {
if ( $ geo ) {
$ data = { geoTrackingEnabled = > "true" } ;
}
else {
$ data = { geoTrackingEnabled = > "false" } ;
}
$ data = encode_json ( $ data ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ request = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'PUT' ,
timeout = > 3 ,
mobileID = > $ mobileID ,
data = > $ data ,
hash = > $ hash ,
callback = > \ & TadoAPI_SetGeoByIdCallback
} ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "PUT setting $data" ;
HttpUtils_NonblockingGet ( $ request ) ;
}
return ;
2019-11-23 10:49:06 +00:00
}
######################################
############ Helpers #################
######################################
2020-04-21 22:40:51 +00:00
sub TadoAPI_ReplaceUmlaute {
my $ string = shift ;
my % umlaute = (
"ä" = > "ae" ,
"Ä" = > "Ae" ,
"ü" = > "ue" ,
"Ü" = > "Ue" ,
"ö" = > "oe" ,
"Ö" = > "Oe" ,
"ß" = > "ss"
) ;
my $ umlautkeys = join ( "|" , keys ( % umlaute ) ) ;
$ string =~ s/($umlautkeys)/$umlaute{$1}/g ;
return $ string ;
2019-11-23 10:49:06 +00:00
}
2020-02-07 13:26:15 +00:00
# helper sub for fhem tablet-ui thermostat widget: set timedZoneOverlay <zoneID> <duration> <setting>
2020-04-21 22:40:51 +00:00
sub TadoAPI_SetTimedZoneOverlay {
my $ hash = shift ;
my $ zoneID = shift ;
my $ duration = shift ;
my $ setting = shift ;
my $ name = $ hash - > { NAME } ;
TadoAPI_SetZoneOverlayById ( $ hash , $ zoneID , $ setting , $ duration ) ;
return ;
2020-02-06 07:39:50 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetHomeId {
# returns first home id only
my $ hash = shift ;
my $ name = $ hash - > { NAME } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
2020-02-06 07:39:50 +00:00
2020-04-21 22:40:51 +00:00
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ DataURL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 2 ,
hash = > $ hash ,
} ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $DataURL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetHomeId: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "URL " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetHomeId: Decode_json failed, invalid json. error:$@"
if $@ ;
$ hash - > { LastRequest } = "error" ;
}
else {
$ hash - > { LastRequest } = "OK" ;
return $ decoded_data - > { 'homes' } - > [ 0 ] - > { 'id' }
if ( exists ( $ decoded_data - > { 'homes' } ) ) ;
}
}
}
return ;
}
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetGeoById {
# returns geo setting and distance from home; takes an item object or querys itself
my $ hash = shift ;
my $ mobileID = shift ;
my $ item = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/mobileDevices } ;
if ( ! defined ( $ item ) ) {
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 4 ,
hash = > $ hash ,
} ;
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetGeoById: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 5 ,
"GetGeoById URL: " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetGeoById: Decode_json failed, invalid json. error:$@\n"
if $@ ;
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetGeoById: Error in UpdateMobileReadingsCallback, Code: "
. $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' } ;
$ hash - > { LastRequest } = "error" ;
}
else {
for my $ item ( @$ decoded_data ) {
if ( $ item - > { 'id' } eq $ mobileID ) {
return my ( $ setting , $ distance ) =
TadoAPI_ParseMobileItem ( $ hash , $ item ) ;
}
}
}
}
}
2019-12-10 09:14:23 +00:00
}
2020-04-21 22:40:51 +00:00
elsif ( defined ( $ item ) ) {
Log3 $ name , 5 ,
"TadoAPI $name" . ": " . "GetGeoById: parsing passed item" ;
return my ( $ setting , $ distance ) =
TadoAPI_ParseMobileItem ( $ hash , $ item ) ;
2019-12-10 09:14:23 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_ParseMobileItem {
my $ hash = shift ;
my $ item = shift ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ name = $ hash - > { NAME } ;
my $ setting = 0 ;
$ setting = 1 if $ item - > { 'settings' } - > { 'geoTrackingEnabled' } ;
my $ distance = "-" ;
$ distance = $ item - > { 'location' } - > { 'relativeDistanceFromHomeFence' }
if $ setting ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate (
$ hash ,
"GeoTracking_" . $ item - > { 'id' } ,
$ item - > { 'settings' } - > { 'geoTrackingEnabled' }
) ;
if ( defined ( $ item - > { 'location' } - > { 'atHome' } )
&& $ item - > { 'location' } - > { 'atHome' } )
{
# present
readingsBulkUpdate ( $ hash , "GeoLocation_" . $ item - > { 'id' } , "present" ) ;
2019-12-10 09:14:23 +00:00
}
2020-04-21 22:40:51 +00:00
elsif ( defined ( $ item - > { 'location' } - > { 'atHome' } ) ) {
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
# away
readingsBulkUpdate ( $ hash , "GeoLocation_" . $ item - > { 'id' } , "away" ) ;
}
else {
# no state
readingsDelete ( $ hash , "GeoLocation_" . $ item - > { 'id' } ) ;
}
readingsBulkUpdate ( $ hash , "GeoDistance_" . $ item - > { 'id' } , $ distance )
if $ setting && $ attr { $ name } { showPosData } ;
readingsDelete ( $ hash , "GeoDistance_" . $ item - > { 'id' } )
if ! defined ( $ attr { $ name } { showPosData } )
|| $ attr { $ name } { showPosData } == 0
|| ! $ setting ;
readingsEndUpdate ( $ hash , 1 ) ;
$ hash - > { LastRequest } = "OK" ;
return ( $ setting , $ distance ) ;
2019-12-10 22:49:02 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetMobileDevices {
my $ hash = shift ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/mobileDevices } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 2 ,
hash = > $ hash
} ;
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetMobileDevices: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
my @ devices = ( ) ;
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "GetMobileDevices: decode_json failed, invalid json. error:$@\n" ;
}
else {
if ( ref ( $ decoded_data ) eq 'ARRAY' ) {
for my $ item ( @$ decoded_data ) {
push @ devices , $ item ;
}
# default case
return @ devices ;
}
elsif ( ref ( $ decoded_data ) eq 'HASH' ) {
# error, api response is a hash in case of error
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetMobileDevices: "
. $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' }
if ( exists ( $ decoded_data - > { 'errors' } ) ) ;
}
}
2019-11-23 10:49:06 +00:00
}
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetZoneCount {
my $ hash = shift ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/zones } ;
my $ zonecount = 0 ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = >
"$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 2 ,
hash = > $ hash
} ;
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetZoneCount: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
my @ devices = ( ) ;
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "GetZoneCount: decode_json failed, invalid json. error:$@\n" ;
}
else {
if ( ref ( $ decoded_data ) eq 'ARRAY' ) {
for my $ item ( @$ decoded_data ) {
$ zonecount + + ;
}
return $ zonecount ;
}
elsif ( ref ( $ decoded_data ) eq 'HASH' ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetZoneCount: "
. $ decoded_data - > { 'errors' } - > [ 0 ] - > { 'code' }
if ( exists ( $ decoded_data - > { 'errors' } ) ) ;
}
}
2019-11-23 10:49:06 +00:00
}
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetZoneNameById {
my $ hash = shift ;
my $ zoneID = shift ;
my $ name = $ hash - > { NAME } ;
my $ zoneName = undef ;
my @ zones = TadoAPI_GetTadoDevices ( $ hash ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
for my $ zone ( @ zones ) {
if ( $ zone - > { 'id' } == $ zoneID ) {
$ zoneName = TadoAPI_ReplaceUmlaute ( $ zone - > { 'name' } ) ;
return $ zoneName ;
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
Log3 $ name , 3 ,
"TadoAPI $name" . ": " . "Error GetZoneNameById: Wrong zone ID ($zoneID)" ;
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetZoneReadingsById {
my $ hash = shift ;
my $ zoneID = shift ;
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/zones/$zoneID/state } ;
my $ temperature = 0 ;
my $ humidity = 0 ;
my $ desiredTemp = 0 ;
my $ currentHeatingPower = 0 ;
my $ overlay = 0 ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = > "$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 4 ,
hash = > $ hash
} ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "GetZoneReadingsById: Error while requesting "
. $ param - > { url }
. " - $err" ;
}
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "GetZoneReadingsById: Zone $zoneID decode_json failed, invalid json. error:$@\n" ;
}
else {
$ temperature = sprintf ( "%.1f" ,
$ decoded_data - > { 'sensorDataPoints' } - > { 'insideTemperature' }
- > { 'celsius' } ) ;
$ humidity = $ decoded_data - > { 'sensorDataPoints' } - > { 'humidity' }
- > { 'percentage' } ;
if ( $ decoded_data - > { 'setting' } - > { 'power' } eq "OFF" ) {
$ desiredTemp = "OFF" ;
}
else {
$ desiredTemp =
$ decoded_data - > { 'setting' } - > { 'temperature' } - > { 'celsius' } ;
}
$ currentHeatingPower =
$ decoded_data - > { 'activityDataPoints' } - > { 'heatingPower' }
- > { 'percentage' } ;
$ overlay = $ decoded_data - > { 'overlayType' } ;
if ( ! defined $ overlay ) { $ overlay = "no overlay" }
return ( $ temperature , $ humidity , $ desiredTemp ,
$ currentHeatingPower , $ overlay ) ;
}
2019-12-10 16:41:08 +00:00
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
return ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_GetTadoDevices {
# returns array with zonenames and zone devices
my $ hash = shift ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
my $ name = $ hash - > { NAME } ;
my $ homeID = $ attr { $ name } { homeID } ;
my $ URL = $ QueryURL . qq{ /$homeID/zones } ;
my $ CurrentTokenData = TadoAPI_LoadToken ( $ hash ) ;
if ( defined ( $ CurrentTokenData ) ) {
my $ param = {
url = > $ URL ,
header = > {
"Content-Type" = > "application/json;charset=UTF-8" ,
"Authorization" = >
"$CurrentTokenData->{'token_type'} $CurrentTokenData->{'access_token'}"
} ,
method = > 'GET' ,
timeout = > 5 ,
hash = > $ hash
} ;
2019-11-23 10:49:06 +00:00
2020-04-21 22:40:51 +00:00
#Log3 $name, 5, 'Blocking GET: ' . Dumper($param);
Log3 $ name , $ reqDebug , "TadoAPI $name" . ": " . "Request $URL" ;
my ( $ err , $ data ) = HttpUtils_BlockingGet ( $ param ) ;
if ( $ err ne "" ) {
Log3 $ name , 3 ,
"TadoAPI $name" . ": "
. "RequestTadoDevices: Error while requesting "
. $ param - > { url }
. " - $err" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
elsif ( $ data ne "" ) {
Log3 $ name , 5 , "url " . $ param - > { url } . " returned: $data" ;
my $ decoded_data = eval { decode_json ( $ data ) } ;
if ( $@ ) {
Log3 $ name , 3 , "TadoAPI $name" . ": "
. "RequestTadoDevices: decode_json failed, invalid json. error:$@\n" ;
}
else {
if ( ref ( $ decoded_data ) eq 'ARRAY' ) {
my @ devices = ( ) ;
for my $ dev ( @$ decoded_data ) {
push @ devices , $ dev ;
}
return @ devices ;
}
}
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
}
return ;
2019-11-23 10:49:06 +00:00
}
######################################################
# storePW & readPW Code geklaut aus 96_SIP.pm :)
######################################################
2020-04-21 22:40:51 +00:00
sub TadoAPI_storePassword {
my $ name = shift ;
my $ password = shift ;
my $ index = "TadoAPI_" . $ name . "_passwd" ;
my $ key = getUniqueId ( ) . $ index ;
2019-11-23 10:49:06 +00:00
my $ e_pwd = "" ;
2020-04-19 22:36:32 +00:00
2020-04-21 22:40:51 +00:00
if ( eval { use Digest::MD5 ; 1 } ) {
$ key = Digest::MD5:: md5_hex ( unpack "H*" , $ key ) ;
2019-11-23 10:49:06 +00:00
$ key . = Digest::MD5:: md5_hex ( $ key ) ;
}
2020-04-19 22:36:32 +00:00
2020-04-21 22:40:51 +00:00
for my $ char ( split // , $ password ) {
my $ encode = chop ( $ key ) ;
$ e_pwd . = sprintf ( "%.2x" , ord ( $ char ) ^ ord ( $ encode ) ) ;
$ key = $ encode . $ key ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
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" ;
2019-11-23 10:49:06 +00:00
}
2020-04-21 22:40:51 +00:00
sub TadoAPI_readPassword {
my $ name = shift ;
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" ;
}
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 ;
}
2019-11-23 10:49:06 +00:00
}
1 ;
= pod
2019-12-14 15:29:46 +00:00
= item device
2019-11-23 10:49:06 +00:00
= item summary integration of the Tado API
= item summary_DE Anbindung der Tado Heizungssteuerung & uuml ; ber API
= begin html
< a name = "TadoAPI" > </a>
<h3> TadoAPI </h3>
<ul>
2019-12-14 15:29:46 +00:00
The TadoAPI module connects your tado devices to FHEM . Most zone readings are shown and desired temperature for a zone can be set . <br>
TadoAPI makes use of the ( unofficial ) tado api and does NOT rely on any addition local client installed . <br>
2019-11-23 10:49:06 +00:00
Notes:
<ul>
2019-12-14 15:29:46 +00:00
<li> JSON has to be installed on the FHEM host . <br>
Please install the module ( e . g . with <code> sudo apt - get install libjson - perl </code> ) or the correct method for the underlying platform /system.</ li >
2019-11-23 10:49:06 +00:00
</ul>
< a name = "TadoAPIdefine" > </a>
<b> Define </b>
<ul>
2019-11-23 11:47:42 +00:00
The username and password must match the username and password used on the Tado website . <br>
After successful define , store PASSWORD with <code> set & lt ; name & gt ; password & lt ; your - tado - password & gt ; </code> . <br>
Note: Password is encrypted and saved in FHEM uniqueID file . All requests to the API are handeld via oauth2 token . <br>
2019-11-23 10:49:06 +00:00
Examples:
<ul> <code>
define & lt ; name & gt ; TadoAPI mail @ example . com [ & lt ; homeID & gt ; ] <br>
</code> </ul>
<br>
</ul>
< a name = "TadoAPIset" > </a>
<b> Set </b>
<ul>
<li>
<code> set & lt ; name & gt ; & lt ; tado password & gt ; </code> <br>
Stores <code> password </code> from tado account encrypted in FHEM . <br>
Without stored password all functions are blocked ! <br>
IMPORTANT : if you rename the fhem Device you must set the password again !
</li>
<li>
<code> set & lt ; name & gt ; update </code> <br>
Reloads all information from the tado installation ( devices , battery state , geolocation , ... ) .
</li>
<li>
2019-11-25 21:02:47 +00:00
<code> set & lt ; name & gt ; setZoneOverlay & lt ; zoneID & gt ; & lt ; setting & gt ; [ & lt ; duration & gt ; ] </code> <br>
2019-12-27 08:56:19 +00:00
Setting: remove = delete overlay ; 0 = heating power off ; & gt ; 1 sets desired temperature to given value ( overlay )
2019-11-23 10:49:06 +00:00
</li>
<li>
<code> set & lt ; name & gt ; setAllOverlays & lt ; setting & gt ; </code> <br>
Same as above , but for all zones <br>
2019-12-27 08:56:19 +00:00
Setting: remove = delete overlay ; 0 = heating power off ; & gt ; 1 sets desired temperature to given value ( overlay ) </li>
2019-11-23 10:49:06 +00:00
<br>
</ul>
<br>
< a name = "TadoAPIattr" > </a>
<b> Attributes </b>
<ul>
<li> homeID <br>
Home ID that will be used for <b> API </b> querys .
</li>
2019-11-25 21:02:47 +00:00
<li> updateIntervall <br>
2019-12-11 12:37:41 +00:00
Intervall ( in seconds ) that is used for polling the <b> tado API </b> to update readings .
</li>
<li> showPosData <br>
If set to <b> 1 </b> readings with relative distance to tado home are shown .
2019-11-25 21:02:47 +00:00
</li>
2019-11-23 10:49:06 +00:00
</ul>
<br>
</ul>
= end html
# Ende der Commandref
2020-04-21 22:40:51 +00:00
= cut