diff --git a/98_TadoAPI.pm b/98_TadoAPI.pm index 5f9e319..841c5d9 100644 --- a/98_TadoAPI.pm +++ b/98_TadoAPI.pm @@ -5,7 +5,7 @@ # # USAGE: Module for FHEM # Info: Turn verbose on for debugging -# +# # REQUIREMENTS: Below modules should be pre-installed. # HTTP::Request::Common # HTTP::Headers @@ -31,1475 +31,1786 @@ use JSON; use POSIX qw(strftime); ####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 $header = {}; -my $reqDebug = 5; +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; # helpers my $apiStatus = 1; my %sets = ( - "zoneUpdate" => "", - "refreshToken" => "noArg", - "password" => "", - "update" => "noArg", - "setGeo" => "", - "setZoneOverlay" => "", - "timedZoneOverlay" => "", - "updateAllOverlays" => "noArg", - "setAllOverlays" => "" - ); - -my %gets = ( - "getZoneDevices" => "noArg", - "getZoneInfo" => "noArg", - "getGeo" => "", - #"getXTest" => "", - "getMobileDevices" => "noArg" + "zoneUpdate" => "", + "refreshToken" => "noArg", + "password" => "", + "update" => "noArg", + "setGeo" => "", + "setZoneOverlay" => "", + "timedZoneOverlay" => "", + "updateAllOverlays" => "noArg", + "setAllOverlays" => "" ); -sub -TadoAPI_Initialize -{ - my $hash = shift; - my $TYPE = "TadoAPI"; +my %gets = ( + "getZoneDevices" => "noArg", + "getZoneInfo" => "noArg", + "getGeo" => "", - $hash->{DefFn} = $TYPE."_Define"; - $hash->{InitFn} = $TYPE."_Init"; - $hash->{SetFn} = $TYPE."_Set"; - $hash->{GetFn} = $TYPE."_Get"; - $hash->{AttrList} = "" - . "homeID " - . "mobileID " - . "showPosData:0,1 " - . "updateIntervall " - . $readingFnAttributes - ; + #"getXTest" => "", + "getMobileDevices" => "noArg" +); + +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; } -sub TadoAPI_Init -{ - my $hash = shift; - my @args = @_; +sub TadoAPI_Init { + my $hash = shift; + my @args = @_; - my $u = "wrong syntax: define TadoAPI []"; - return $u if(int(@args) < 2); - return; + my $u = + "wrong syntax: define TadoAPI []"; + return $u if ( int(@args) < 2 ); + return; } -sub TadoAPI_Define -{ - my $hash = shift; - my $def = shift; +sub TadoAPI_Define { + my $hash = shift; + my $def = shift; - my @a = split( "[ \t]+", $def ); - my $name = shift @a; - my $type = shift @a; - my $tokenFileName = $tokenFile."_".$name; + my @a = split( "[ \t]+", $def ); + my $name = shift @a; + my $type = shift @a; + my $tokenFileName = $tokenFile . "_" . $name; - return "Invalid number of arguments: " + return "Invalid number of arguments: " . "define TadoAPI []" - if ( int(@a) < 1 ); + if ( int(@a) < 1 ); - my ( $user, $homeID ) = @a; - Log3 $name, 3, "TadoAPI_Define $name: called "; - $hash->{STATE}="defined"; + my ( $user, $homeID ) = @a; + Log3 $name, 3, "TadoAPI_Define $name: called "; + $hash->{STATE} = "defined"; - # Initialize the device - return $@ unless ( FHEM::Meta::SetInternals($hash) ); + # Initialize the device + return $@ unless ( FHEM::Meta::SetInternals($hash) ); - $hash->{TADO_USER} = $user; - $hash->{TOKEN_FILE} = $tokenFileName; + $hash->{TADO_USER} = $user; + $hash->{TOKEN_FILE} = $tokenFileName; - my @args = ($homeID); + my @args = ($homeID); - if ($main::init_done) { - # do something? - return TadoAPI_Catch($@) if $@; - } + if ($main::init_done) { - my $password = TadoAPI_readPassword($name); - - 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; - } + # do something? + return TadoAPI_Catch($@) if $@; } - }else{ - $hash->{STATE}="no password set"; - } - return; + + my $password = TadoAPI_readPassword($name); + + 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; + } + } + } + else { + $hash->{STATE} = "no password set"; + } + return; } -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; - my $message = undef; +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; + 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 ); } - return "Unknown argument $a[1], choose one of " . join(" ", @cmds); - } - 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; + 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; + } } - } - 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); + 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); + } + 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"; + 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); + } + 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"; + 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 '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' ) { + } + elsif ( $cmd eq 'updateAllOverlays' ) { Log3 $name, 5, "TadoAPI $name" . ": " . "processing ($cmd)"; TadoAPI_GetAllZoneOverlays($hash); Log3 $name, 4, "TadoAPI $name" . ": " . "$cmd finished\n"; - } elsif( $cmd eq 'refreshToken' ) { + } + elsif ( $cmd eq 'refreshToken' ) { 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"; - } elsif( $cmd eq 'update' ) { + } + elsif ( $cmd eq 'update' ) { Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)"; TadoAPI_UpdateFn($hash); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; - } elsif( $cmd eq 'zoneUpdate' ) { + } + elsif ( $cmd eq 'zoneUpdate' ) { Log3 $name, 5, "TadoAPI $name" . ": " . "processing ($cmd)"; - 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); + 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"; - } + $message = "OK"; + } + } + else { + return "Wrong ZoneID"; + } Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; - } elsif( $cmd eq 'password' ) { + } + elsif ( $cmd eq 'password' ) { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)"; + # name und cmd überspringen shift @a; shift @a; + # den Rest der das passwort enthält, als ein String - $subcmd = join(" ",@a); - $message = TadoAPI_storePassword($name,$subcmd); + $subcmd = join( " ", @a ); + $message = TadoAPI_storePassword( $name, $subcmd ); # start the status update timer RemoveInternalTimer($hash); InternalTimer( gettimeofday() + 10, "TadoAPI_Update", $hash, 0 ); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; - } - return $message if $message; - return TadoAPI_Catch($@) if $@; - return; - } - -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 $message = undef; - - if(!defined($gets{$cmd})) { - my @cmds = (); - for my $key (sort keys %gets) { - push @cmds, $gets{$key} ? $key.":".join(",",$gets{$key}) : $key; - } - return "Unknown argument $a[1], choose one of " . join(" ", @cmds); - } - - 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; - }; } return $message if $message; return TadoAPI_Catch($@) if $@; return; - } +} + +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; + } + return "Unknown argument $a[1], choose one of " . join( " ", @cmds ); + } + + 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; + }; + } + return $message if $message; + return TadoAPI_Catch($@) if $@; + return; + } } sub TadoAPI_Catch { - my $exception = shift; - if ($exception) { - $exception =~ /^(.*)( at.*FHEM.*)$/; - return $1; - } - return; + my $exception = shift; + if ($exception) { + $exception =~ /^(.*)( at.*FHEM.*)$/; + return $1; + } + return; } -sub TadoAPI_Undefine($$) { - my ( $hash, $name ) = @_; +sub TadoAPI_Undefine { + my $hash = shift; + my $name = shift; RemoveInternalTimer($hash); + #todo remove tokenfile return; } -sub TadoAPI_CheckStatus(@){ - my ($hash) = @_; - my $name = $hash->{NAME}; +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; + # 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; } -sub TadoAPI_LoadToken(@){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $tokenFileName = $tokenFile."_".$name; - my $tokenLifeTime = $hash->{TOKEN_LIFETIME}; - $tokenLifeTime = 0 if(!defined $tokenLifeTime || $tokenLifeTime eq ''); - my $Token = undef; +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 ($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); - } + 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; } - return $Token if $Token; - } - TadoAPI_CheckStatus($hash); - return; + TadoAPI_CheckStatus($hash); + return; } -sub TadoAPI_NewTokenRequest(@) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $username = $hash->{TADO_USER}; - my $password = TadoAPI_readPassword($name); - my $tokenFileName = $tokenFile."_".$name; +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()"; + 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 $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 - }; + 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); + #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; + if ( $err ne "" ) { + Log3 $name, 3, + "TadoAPI $name" . ": " + . "NewTokenRequest: Error while requesting " + . $param->{url} + . " - $err"; } - } - return; -} + 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); -sub TadoAPI_TokenRefresh(@) { - my ($hash) = @_; - 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; + # 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; + } } - } - return; + return; } -sub TadoAPI_Update(@){ - my ($hash) = @_; - my $name = $hash->{NAME}; +sub TadoAPI_TokenRefresh { + my $hash = shift; + my $name = $hash->{NAME}; + my $tokenFileName = $tokenFile . "_" . $name; + my $Token = undef; - Log3 $name, 5, "TadoAPI $name" . ": " . "TadoAPI_Update called"; + # load token + open( my $TOKENFILE, q{<}, $tokenFileName ) or croak("ERROR: $!"); + eval { $Token = decode_json(<$TOKENFILE>) }; + close($TOKENFILE); - my $nextTimer = "none"; - my $intervall = 300; + my $data = { + client_id => $client_id, + client_secret => $client_secret, + scope => $scope, + grant_type => 'refresh_token', + refresh_token => $Token->{'refresh_token'} + }; - $intervall = $attr{$name}{updateIntervall} if (defined($attr{$name}{updateIntervall}) && $attr{$name}{updateIntervall} =~ m/^-?\d+$/); - $nextTimer = gettimeofday() + $intervall; - $hash->{NEXT_UPDATE} = localtime($nextTimer); + my $param = { + url => $AuthURL, + method => 'POST', + timeout => 5, + hash => $hash, + data => $data + }; - Log3 $name, 5, "TadoAPI $name" . ": " . "Next Timer = $nextTimer"; + #Log3 $name, 5, 'Blocking GET TokenRefresh: ' . Dumper($param); + Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $AuthURL"; + my ( $err, $returnData ) = HttpUtils_BlockingGet($param); - RemoveInternalTimer($hash); - InternalTimer( $nextTimer, "TadoAPI_Update", $hash, 0 ); + if ( $err ne "" ) { + Log3 $name, 3, + "TadoAPI $name" . ": " + . "TokenRefresh: Error in token retrival while requesting " + . $param->{url} + . " - $err"; + $hash->{STATE} = "error"; + } - # update subs - TadoAPI_UpdateFn($hash); - return; + 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; + } + } + return; +} + +sub TadoAPI_Update { + my $hash = shift; + my $name = $hash->{NAME}; + + Log3 $name, 5, "TadoAPI $name" . ": " . "TadoAPI_Update called"; + + my $nextTimer = "none"; + my $intervall = 300; + + $intervall = $attr{$name}{updateIntervall} + if ( defined( $attr{$name}{updateIntervall} ) + && $attr{$name}{updateIntervall} =~ m/^-?\d+$/ ); + $nextTimer = gettimeofday() + $intervall; + $hash->{NEXT_UPDATE} = localtime($nextTimer); + + Log3 $name, 5, "TadoAPI $name" . ": " . "Next Timer = $nextTimer"; + + RemoveInternalTimer($hash); + InternalTimer( $nextTimer, "TadoAPI_Update", $hash, 0 ); + + # update subs + TadoAPI_UpdateFn($hash); + return; } ######################## tado methods ######################## ############################################################## -sub TadoAPI_SetZoneOverlayById(@){ - my ($hash, $zoneID, $setting, $duration) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL = $QueryURL . qq{/$homeID/zones/$zoneID/overlay}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); +sub TadoAPI_SetZoneOverlayById { + my $hash = shift; + my $zoneID = shift; + my $setting = shift; + my $duration = shift; - if(defined($CurrentTokenData)){ - my $method = ""; - my $myjson =undef; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL = $QueryURL . qq{/$homeID/zones/$zoneID/overlay}; + my $CurrentTokenData = TadoAPI_LoadToken($hash); - Log3 $name, 5, "TadoAPI $name" . ": SetOverlay for Zone $zoneID (Setting: " . $setting . ") - " . "query-URL: $URL"; + if ( defined($CurrentTokenData) ) { + my $method = ""; + my $myjson = undef; - my $dt = time(); - $dt += $duration if defined($duration); + Log3 $name, 5, + "TadoAPI $name" + . ": SetOverlay for Zone $zoneID (Setting: " + . $setting . ") - " + . "query-URL: $URL"; - delete($hash->{helper}{LockedZones}{$zoneID}); + my $dt = time(); + $dt += $duration if defined($duration); - # 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"; - } - # 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 + delete( $hash->{helper}{LockedZones}{$zoneID} ); + + # 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"; } - }, - 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 + + # 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'}" }, - }, - termination => { - type => "MANUAL" - }, + method => $method, + timeout => 5, + callback => \&Tado_UpdateZoneOverlayCallback, + hash => $hash, + setting => $setting, + zoneID => $zoneID, + data => $myjson }; - } + + Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; + HttpUtils_NonblockingGet($request); } - - $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'}" }, - method => $method, - timeout => 5, - callback => \&Tado_UpdateZoneOverlayCallback, - hash => $hash, - setting => $setting, - zoneID => $zoneID, - data => $myjson - }; - - Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; - HttpUtils_NonblockingGet($request); - } - return; + return; } -sub TadoAPI_SetAllOverlays(@){ - my ($hash, $setting, $duration) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my @zones = TadoAPI_GetTadoDevices($hash); +sub TadoAPI_SetAllOverlays { + my $hash = shift; + my $setting = shift; + my $duration = shift; - 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); + 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 ); + } } - } - return; + return; } -sub TadoAPI_GetAllZoneOverlays(@){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my @zones = TadoAPI_GetTadoDevices($hash); +sub TadoAPI_GetAllZoneOverlays { + my $hash = shift; - 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; + 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; } -sub TadoAPI_UpdateFn(@){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); - my $homeID = $attr{$name}{homeID}; +sub TadoAPI_UpdateFn { + my $hash = shift; - 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); + my $name = $hash->{NAME}; + my $CurrentTokenData = TadoAPI_LoadToken($hash); + my $homeID = $attr{$name}{homeID}; - # 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; + 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; } ######################################################################################################################################################################## # Callback Subs ######################################################################################################################################################################## -sub TadoAPI_callback($){ - my ($param, $err, $data) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - $param->{code} = 0 unless defined $param->{code}; +sub TadoAPI_callback { + my $param = shift; + my $err = shift; + my $data = shift; - if($param->{code} == 401 || $param->{code} == 400){ - $apiStatus = 1; - $hash->{STATE}="reachable"; - Log3 $name, 5, "TadoAPI $name" . ": " . "API is reachable. Callback Status: " . $param->{code}; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + $param->{code} = 0 unless defined $param->{code}; - }else{ - $apiStatus = 0; - $hash->{STATE}="error"; - Log3 $name, 3, "TadoAPI $name" . ": " . "API error: apiStatus $apiStatus ($err)"; - } - return; + 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; } -sub TadoAPI_UpdateAllZoneReadingsCallback($){ - my ($param, $err, $data) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; +sub TadoAPI_UpdateAllZoneReadingsCallback { + my $param = shift; + my $err = shift; + my $data = shift; - 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) }; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; - # if api returns error - eval { my $error = @$decoded_data; }; + 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 ($@){ - 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"; - }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 "; + # if api returns error + eval { my $error = @$decoded_data; }; - my ($temperature, $humidity, $desiredTemp, $currentHeatingPower, $overlay ) = TadoAPI_GetZoneReadingsById($hash, $zoneID); + 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"; + } + 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"; + } + return; +} + +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"; + + } + + # 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 - 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); + readingsBeginUpdate($hash); - # iterate through all devices in zone - my $devices = $zone->{'devices'}; - for my $device ( @$devices ){ - readingsBulkUpdate($hash, "Battery_" . $device->{'serialNo'}, $device->{'batteryState'}); + # 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" ); } - } - readingsEndUpdate( $hash, 1 ); } - my $zonecount = TadoAPI_GetZoneCount($hash); - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "ActiveZones", $zonecount); - readingsEndUpdate( $hash, 0 ); - $hash->{LastRequest}="OK"; - } - return; + return; } -sub Tado_UpdateZoneOverlayCallback($) -{ - my ($param, $err, $data) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - my $zoneID = $param->{zoneID}; - my $setting = $param->{setting}; +sub TadoAPI_LogInfoCallback { + my $param = shift; + my $err = shift; + my $data = shift; - if($err ne "") - { - Log3 $name, 3, "Error in UpdateZoneOverlayCallback while requesting ".$param->{url}." - $err"; - } + my $hash = $param->{hash}; + my $name = $hash->{NAME}; - elsif($data ne "") - { - Log3 $name, 5, "url ".$param->{url}." returned: $data"; - Log3 $name, 3, "TadoAPI $name" . ": " . "set (async) Overlay for Zone $zoneID to: $setting"; - - } - # 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; -} - -sub TadoAPI_LogInfoCallback($){ - my ($param, $err, $data) = @_; - 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; -} - -sub TadoAPI_SetGeoByIdCallback($){ - my ($param, $err, $data) = @_; - 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"; - } - return; -} - -sub TadoAPI_UpdateMobileReadingsCallback($){ - my ($param, $err, $data) = @_; - my $hash = $param->{hash}; - my $name = $hash->{NAME}; - - if($err ne "") - { - Log3 $name, 3, "Error in UpdateMobileReadingsCallback 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" . ": " . "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); - } - } - } - return; -} - -sub TadoAPI_GetZoneInfo(@) { - my ($hash) = @_; - 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); + if ( $err ne "" ) { + Log3 $name, 3, + "Error in LogInfoCallback while requesting " + . $param->{url} + . " - $err"; } - # 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); + elsif ( $data ne "" ) { + Log3 $name, 3, + "TadoAPI $name" . ": " . $param->{infotext} . ":\n" . $data . "\n"; } - } - return; + return; } -sub TadoAPI_SetGeoById(@){ - my ($hash, $mobileID, $geo) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL . qq{/$homeID/mobileDevices/$mobileID/settings}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); - my $data = {}; +sub TadoAPI_SetGeoByIdCallback { + my $param = shift; + my $err = shift; + my $data = shift; - if(defined($CurrentTokenData)){ - if($geo){ - $data = { geoTrackingEnabled=>"true" }; - }else{ - $data = { geoTrackingEnabled=>"false" }; + 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"; + } + return; +} + +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"; } - $data = encode_json($data); + elsif ( $data ne "" ) { + Log3 $name, 5, "url " . $param->{url} . " returned: $data"; + my $decoded_data = eval { decode_json($data) }; - 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; + # 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 ); + } + } + } + return; +} + +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); + } + } + 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); + + 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; } ###################################### ############ Helpers ################# ###################################### -sub TadoAPI_ReplaceUmlaute(@) { - my ($string) = @_; - my %umlaute = ("ä" => "ae", "Ä" => "Ae", "ü" => "ue", "Ü" => "Ue", "ö" => "oe", "Ö" => "Oe", "ß" => "ss" ); - my $umlautkeys = join ("|", keys(%umlaute)); +sub TadoAPI_ReplaceUmlaute { + my $string = shift; - $string =~ s/($umlautkeys)/$umlaute{$1}/g; - return $string; + my %umlaute = ( + "ä" => "ae", + "Ä" => "Ae", + "ü" => "ue", + "Ü" => "Ue", + "ö" => "oe", + "Ö" => "Oe", + "ß" => "ss" + ); + my $umlautkeys = join( "|", keys(%umlaute) ); + + $string =~ s/($umlautkeys)/$umlaute{$1}/g; + return $string; } # helper sub for fhem tablet-ui thermostat widget: set timedZoneOverlay -sub TadoAPI_SetTimedZoneOverlay(@){ - my ($hash, $zoneID, $duration, $setting) = @_; - my $name = $hash->{NAME}; - TadoAPI_SetZoneOverlayById($hash, $zoneID, $setting, $duration); - return; +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; } - -sub TadoAPI_GetHomeId(@){ - # returns first home id only - my ($hash) = @_; - my $name = $hash->{NAME}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); - - 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, - }; - - #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); - Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $DataURL"; - my ($err, $data) = HttpUtils_BlockingGet($param); - - 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; -} - -sub TadoAPI_GetGeoById(@){ - # returns geo setting and distance from home; takes an item object or querys itself - my ($hash, $mobileID, $item) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL.qq{/$homeID/mobileDevices}; - - if(!defined($item)){ +sub TadoAPI_GetHomeId { + # returns first home id only + my $hash = shift; + my $name = $hash->{NAME}; 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, - }; + 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, + }; - #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); - Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; - my ($err, $data) = HttpUtils_BlockingGet($param); + #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); + Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $DataURL"; + 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); + 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'} ) ); } - } } - } } - }elsif(defined($item)){ - Log3 $name, 5, "TadoAPI $name" . ": " . "GetGeoById: parsing passed item"; - return my ($setting, $distance) = TadoAPI_ParseMobileItem($hash, $item); - } - return; + return; } -sub TadoAPI_ParseMobileItem(@){ - my ($hash, $item) = @_; - my $name = $hash->{NAME}; - my $setting = 0; - $setting = 1 if $item->{'settings'}->{'geoTrackingEnabled'}; - my $distance = "-"; - $distance = $item->{'location'}->{'relativeDistanceFromHomeFence'} if $setting; +sub TadoAPI_GetGeoById { - 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"); - }elsif(defined($item->{'location'}->{'atHome'})){ - # 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 ); +# returns geo setting and distance from home; takes an item object or querys itself + my $hash = shift; + my $mobileID = shift; + my $item = shift; - $hash->{LastRequest}="OK"; - return ($setting, $distance); + 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 ); + } + } + } + } + } + } + elsif ( defined($item) ) { + Log3 $name, 5, + "TadoAPI $name" . ": " . "GetGeoById: parsing passed item"; + return my ( $setting, $distance ) = + TadoAPI_ParseMobileItem( $hash, $item ); + } + return; } -sub TadoAPI_GetMobileDevices(@) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL . qq{/$homeID/mobileDevices}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); +sub TadoAPI_ParseMobileItem { + my $hash = shift; + my $item = shift; - 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 - }; + my $name = $hash->{NAME}; + my $setting = 0; + $setting = 1 if $item->{'settings'}->{'geoTrackingEnabled'}; + my $distance = "-"; + $distance = $item->{'location'}->{'relativeDistanceFromHomeFence'} + if $setting; - #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 "") + readingsBeginUpdate($hash); + readingsBulkUpdate( + $hash, + "GeoTracking_" . $item->{'id'}, + $item->{'settings'}->{'geoTrackingEnabled'} + ); + if ( defined( $item->{'location'}->{'atHome'} ) + && $item->{'location'}->{'atHome'} ) { - 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'})); - } - } + # present + readingsBulkUpdate( $hash, "GeoLocation_" . $item->{'id'}, "present" ); } - } - return; + elsif ( defined( $item->{'location'}->{'atHome'} ) ) { + + # 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 ); } -sub TadoAPI_GetZoneCount(@) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL.qq{/$homeID/zones}; - my $zonecount = 0; - my $CurrentTokenData = TadoAPI_LoadToken($hash); +sub TadoAPI_GetMobileDevices { + my $hash = shift; - 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 - }; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL = $QueryURL . qq{/$homeID/mobileDevices}; + my $CurrentTokenData = TadoAPI_LoadToken($hash); - #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); - Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; - my ($err, $data) = HttpUtils_BlockingGet($param); + 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 + }; - 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) }; + #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); + Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; + my ( $err, $data ) = HttpUtils_BlockingGet($param); - 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'})); + 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'} ) ); + } + } } - } } - } - return; + return; } -sub TadoAPI_GetZoneNameById(@) { - my ($hash, $zoneID) = @_; - my $name = $hash->{NAME}; - my $zoneName = undef; - my @zones = TadoAPI_GetTadoDevices($hash); +sub TadoAPI_GetZoneCount { + my $hash = shift; - for my $zone ( @zones ){ - if ($zone->{'id'} == $zoneID){ - $zoneName = TadoAPI_ReplaceUmlaute($zone->{'name'}); - return $zoneName; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL = $QueryURL . qq{/$homeID/zones}; + my $zonecount = 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 => 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'} ) ); + } + } + } } - } - Log3 $name, 3, "TadoAPI $name" . ": " . "Error GetZoneNameById: Wrong zone ID ($zoneID)"; - return; + return; } -sub TadoAPI_GetZoneReadingsById(@){ - my ($hash, $zoneID) = @_; - 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); +sub TadoAPI_GetZoneNameById { + my $hash = shift; + my $zoneID = shift; - 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 - }; + my $name = $hash->{NAME}; + my $zoneName = undef; + my @zones = TadoAPI_GetTadoDevices($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" . ": " . "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'}; + for my $zone (@zones) { + if ( $zone->{'id'} == $zoneID ) { + $zoneName = TadoAPI_ReplaceUmlaute( $zone->{'name'} ); + return $zoneName; } - $currentHeatingPower = $decoded_data->{'activityDataPoints'}->{'heatingPower'}->{'percentage'}; - $overlay = $decoded_data->{'overlayType'}; - if (!defined $overlay) {$overlay = "no overlay"}; - - return ($temperature, $humidity, $desiredTemp, $currentHeatingPower, $overlay ); - } } - } - return; + Log3 $name, 3, + "TadoAPI $name" . ": " . "Error GetZoneNameById: Wrong zone ID ($zoneID)"; + return; } -sub TadoAPI_GetTadoDevices(@) { - # returns array with zonenames and zone devices - my ($hash) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL . qq{/$homeID/zones}; - my $CurrentTokenData = TadoAPI_LoadToken($hash); +sub TadoAPI_GetZoneReadingsById { + my $hash = shift; + my $zoneID = shift; - 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 - }; + 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); - #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); - Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; - my ($err, $data) = HttpUtils_BlockingGet($param); + 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 + }; - if($err ne "") - { - Log3 $name, 3, "TadoAPI $name" . ": " . "RequestTadoDevices: Error while requesting ".$param->{url}." - $err"; - } - elsif($data ne "") - { - Log3 $name, 5, "url ".$param->{url}." returned: $data"; - my $decoded_data = eval { decode_json($data) }; + #Log3 $name, 5, 'Blocking GET: ' . Dumper($param); + Log3 $name, $reqDebug, "TadoAPI $name" . ": " . "Request $URL"; + my ( $err, $data ) = HttpUtils_BlockingGet($param); - 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; + 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 ); + } } - } - } - return; + } + return; +} + +sub TadoAPI_GetTadoDevices { + # returns array with zonenames and zone devices + my $hash = shift; + + 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 + }; + + #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"; + } + 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; + } + } + } + } + return; } ###################################################### # storePW & readPW Code geklaut aus 96_SIP.pm :) ###################################################### -sub TadoAPI_storePassword($$) -{ - my ($name, $password) = @_; - my $index = "TadoAPI_".$name."_passwd"; - my $key = getUniqueId().$index; +sub TadoAPI_storePassword { + my $name = shift; + my $password = shift; + + 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); + 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; + 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"; + 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; +sub TadoAPI_readPassword { + my $name = shift; + + my $index = "TadoAPI_" . $name . "_passwd"; + my $key = getUniqueId() . $index; - my ($password, $error); + my ( $password, $error ); - #Log3 $name,5,"$name, read user password from FhemUtils/uniqueID Key $key"; - ($error, $password) = getKeyValue($index); + #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($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); - } + 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 = ''; + 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; - } + 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; + } } 1; @@ -1582,4 +1893,4 @@ sub TadoAPI_readPassword($) =end html # Ende der Commandref -=cut \ No newline at end of file +=cut