diff --git a/98_TadoAPI.pm b/98_TadoAPI.pm index 3f5d915..1167538 100644 --- a/98_TadoAPI.pm +++ b/98_TadoAPI.pm @@ -60,14 +60,17 @@ my %sets = ( "getTemperature" => "", "refreshToken" => "noArg", "password" => "", - "setGeo" => "on,off", - "setOverlay" => "20,21,22,off" + "update" => "noArg", + "setGeo" => "", + "setOverlay" => "" ); my %gets = ( - "getHomeMode" => "noArg", + "getZoneDevices" => "", "getZoneInfo" => "noArg", - "getGeo" => "noArg" + "getGeo" => "", + #"getXTest" => "noArg", + "getMobileDevices" => "noArg" ); @@ -158,22 +161,24 @@ sub TadoAPI_Set(@) { } if( $cmd eq 'setGeo' ) { - if( $value eq "on" ) { + 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_SetGeo($hash, 1); + TadoAPI_SetGeoById($hash, $value, 1); } else { Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)"; - TadoAPI_SetGeo($hash, 0); + TadoAPI_SetGeoById($hash, $value, 0); } - TadoAPI_GetGeo($hash); + TadoAPI_GetGeoById($hash, $value); return undef; } elsif( $cmd eq 'setOverlay' ) { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; - if( $value > 10 ) { - TadoAPI_SetOverlay($hash, $value); + return "Need at least two parameters (ID, Setting)" if(@a < 4); + if( $a[3] > 10 ) { + TadoAPI_SetOverlay($hash, $value, $a[3]); } else { - TadoAPI_SetOverlay($hash, "off"); + TadoAPI_SetOverlay($hash, $value, "off"); } Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; return undef; @@ -185,6 +190,11 @@ sub TadoAPI_Set(@) { TadoAPI_Connect($hash); return undef; + } elsif( $cmd eq 'update' ) { + Log3 $name, 3, "TadoAPI: set $name: processing ($cmd)"; + TadoAPI_UpdateFn($hash); + return undef; + } elsif( $cmd eq 'getTemperature' ) { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; return "ZoneID as parameter needed" if (!$value); @@ -233,19 +243,46 @@ sub TadoAPI_Get(@) { } if($cmd =~ /\Qget\E/) { - eval { + 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)" if $debug; - TadoAPI_GetGeo($hash); + TadoAPI_GetGeoById($hash, $value); Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; last; }; - $cmd eq "getHomeMode" and do { + $cmd eq "getMobileDevices" and do { Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; - TadoAPI_GetHomeMode($hash); + my @data = TadoAPI_RequestMobileDevices($hash); + $message = "Device List:\n"; + foreach my $item ( @data ){ + print "\n"; + $message =+ $message . $item->{'name'} . ": " . $item->{'id'} . "\n"; + }; + Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; + last; + }; + + $cmd eq "getXTest" and do { + Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; + TadoAPI_UpdateFn($hash); + Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; + last; + }; + + $cmd eq "getZoneDevices" and do { + Log3 $name, 3, "TadoAPI $name" . ": " . "processing ($cmd)" if $debug; + return "ID parameter missing (set getZoneDevices zoneID)" if(@a < 3); + TadoAPI_Connect($hash); + my ($devcount, $deviceList) = TadoAPI_GetZoneDevicesById($hash, $value); + $message= "There are " . $devcount . " Tado-Device(s) in this Zone.\n"; + foreach my $item ( @$deviceList ){ + $message =+ $message . $item->{'serialNo'} . " Battery: " . $item->{'batteryState'} . "\n"; + } Log3 $name, 3, "TadoAPI $name" . ": " . "$cmd finished\n"; last; }; @@ -263,7 +300,7 @@ sub TadoAPI_Get(@) { last; }; } - }; + return $message if $message; return TadoAPI_Catch($@) if $@; return undef; @@ -289,75 +326,7 @@ sub TadoAPI_Undefine($$) { return undef; } -###################################################### -# storePW & readPW Code geklaut aus 96_SIP.pm :) -###################################################### -sub TadoAPI_storePassword($$) -{ - my ($name, $password) = @_; - my $index = "TadoAPI_".$name."_passwd"; - my $key = getUniqueId().$index; - my $e_pwd = ""; - - if (eval "use Digest::MD5;1") - { - $key = Digest::MD5::md5_hex(unpack "H*", $key); - $key .= Digest::MD5::md5_hex($key); - } - - for my $char (split //, $password) - { - my $encode=chop($key); - $e_pwd.=sprintf("%.2x",ord($char)^ord($encode)); - $key=$encode.$key; - } - my $error = setKeyValue($index, $e_pwd); - return "error while saving TadoAPI password : $error" if(defined($error)); - return "TadoAPI password successfully saved in FhemUtils/uniqueID Key $index"; -} - -sub TadoAPI_readPassword($) -{ - my ($name) = @_; - my $index = "TadoAPI_".$name."_passwd"; - my $key = getUniqueId().$index; - - my ($password, $error); - - #Log3 $name,5,"$name, read user password from FhemUtils/uniqueID Key $key"; - ($error, $password) = getKeyValue($index); - - if ( defined($error) ) - { - Log3 $name,3, "$name, cant't read Tado password from FhemUtils/uniqueID: $error"; - return undef; - } - - if ( defined($password) ) - { - if (eval "use Digest::MD5;1") - { - $key = Digest::MD5::md5_hex(unpack "H*", $key); - $key .= Digest::MD5::md5_hex($key); - } - - my $dec_pwd = ''; - - for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g)) - { - my $decode=chop($key); - $dec_pwd.=chr(ord($char)^ord($decode)); - $key=$decode.$key; - } - return $dec_pwd; - } - else - { - Log3 $name,3,"$name, no Tado password found in FhemUtils/uniqueID"; - return undef; - } -} sub TadoAPI_callback($$$){ my ($param, $err, $data) = @_; @@ -551,7 +520,7 @@ sub TadoAPI_Update(@){ InternalTimer( $nextTimer, "TadoAPI_Update", $hash, 0 ); # update subs - TadoAPI_GetUpdate($hash); + TadoAPI_UpdateFn($hash); return undef; } @@ -559,16 +528,12 @@ sub TadoAPI_Update(@){ ######################## tado methods ######################## ############################################################## sub TadoAPI_SetOverlay(@){ - my ($hash, $setting) = @_; + my ($hash, $zoneID, $setting) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; - my $URL = $QueryURL . qq{/$homeID/zones/1/overlay}; - - # todo make dynamic - my $zoneID = 1; - + my $URL = $QueryURL . qq{/$homeID/zones/$zoneID/overlay}; - Log3 $name, 3, "TadoAPI $name" . ": SetOverlay (Setting: " . $setting . ") - " . "query-URL: $URL" if $debug; + Log3 $name, 3, "TadoAPI $name" . ": SetOverlay for Zone $zoneID (Setting: " . $setting . ") - " . "query-URL: $URL" if $debug; if($apiStatus == 1){ TadoAPI_Connect($hash); @@ -616,7 +581,7 @@ sub TadoAPI_SetOverlay(@){ if($res->is_success){ print "\n Retriving State:\n" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Retriving state:\n" . $res->content if $debug; - Log3 $name, 3, "TadoAPI $name" . ": " . "Set Overlay setting to: $setting"; + Log3 $name, 3, "TadoAPI $name" . ": " . "Set Overlay for Zone $zoneID to: $setting"; # write readings readingsBeginUpdate($hash); @@ -631,109 +596,92 @@ sub TadoAPI_SetOverlay(@){ } } - -sub TadoAPI_GetUpdate(@){ +sub TadoAPI_UpdateFn(@){ my ($hash) = @_; my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $mobileID = $attr{$name}{mobileID}; - my $URL=$QueryURL.qq{/$homeID/zones/1/state}; - my $deviceURL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; - my $setting = 0; - my $tadoMode = 0; - my $success = 0; TadoAPI_Connect($hash); - my $zonecount = TadoAPI_GetZoneCount($hash); - # zone specific updates if($apiStatus == 1){ - $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); - $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); - $UserAgent->default_headers($header); - - $Request = GET($URL); - $Response = $UserAgent->request($Request); - - if($Response->is_success){ - my $ResponseData = decode_json($Response->content); - $tadoMode = $ResponseData->{'tadoMode'}; - $success = 1; - }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: [Authentication Error]". $Response->status_line; - $success = 0; + my @zones = TadoAPI_RequestZones($hash); + my @devices = TadoAPI_RequestTadoDevices($hash); + my $zonecount = @zones; + + for (my $i=0; $i < $zonecount; $i++) { + print "HomeMode_" . encode("UTF-8", $devices[$i]->{'name'}) . $zones[$i]->{'tadoMode'} . "\n" if $debug; + my $overlay = $zones[$i]->{'overlayType'}; + if (!defined $overlay) {$overlay = "no overlay"}; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "HomeMode_" . encode("UTF-8", $devices[$i]->{'name'}), $zones[$i]->{'tadoMode'}); + readingsBulkUpdate($hash, "Temperatur_" . encode("UTF-8", $devices[$i]->{'name'}), $zones[$i]->{'sensorDataPoints'}->{'insideTemperature'}->{'celsius'}); + readingsBulkUpdate($hash, "Luftfeuchtigkeit_" . encode("UTF-8", $devices[$i]->{'name'}), $zones[$i]->{'sensorDataPoints'}->{'humidity'}->{'percentage'}); + readingsBulkUpdate($hash, "Heizleistung_" . encode("UTF-8", $devices[$i]->{'name'}), $zones[$i]->{'activityDataPoints'}->{'heatingPower'}->{'percentage'}); + readingsBulkUpdate($hash, "OverlayType_" . encode("UTF-8", $devices[$i]->{'name'}), $overlay); + readingsBulkUpdate($hash, "DesiredTemp_" . encode("UTF-8", $devices[$i]->{'name'}), $zones[$i]->{'setting'}->{'temperature'}->{'celsius'}); + readingsEndUpdate( $hash, 1 ); } - # device specific updates - $Request = GET($deviceURL); - $Response = $UserAgent->request($Request); + # mobile devices + my @mobDev = TadoAPI_RequestMobileDevices($hash); + my $mobDevCount = @mobDev; - if($Response->is_success && $success){ - my $ResponseData = decode_json($Response->content); - $setting = $ResponseData->{'geoTrackingEnabled'}; - Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: Actual geo setting for $mobileID is: $setting" if $debug; - $success = 1; - }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "UpdateFn: [Authentication Error]"; - $success = 0; + for (my $i=0; $i < $mobDevCount; $i++) { + print "Geolocation_" . encode("UTF-8", $mobDev[$i]->{'name'}) . $mobDev[$i]->{'settings'}->{'geoTrackingEnabled'} . "\n" if $debug; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "Geolocation_" . encode("UTF-8", $mobDev[$i]->{'id'}), $mobDev[$i]->{'settings'}->{'geoTrackingEnabled'}); + readingsEndUpdate( $hash, 1 ); } - if ($success){ - # readings update - for (my $i=1; $i <= TadoAPI_GetZoneCount($hash); $i++) { - TadoAPI_GetZoneReadingsById($hash, $i); - } - - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "Geolocation", $setting); - readingsBulkUpdate($hash, "HomeMode", $tadoMode); - readingsBulkUpdate($hash, "ActiveZones", $zonecount); + # tado devices + for (my $i=0; $i < $zonecount; $i++) { + my $deviceList = $devices[$i]->{'devices'}; + foreach my $dev ( @$deviceList ){ + print "Battery_" . encode("UTF-8", $dev->{'serialNo'}) . $dev->{'batteryState'} . "\n" if $debug; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "Battery_" . $dev->{'serialNo'}, $dev->{'batteryState'}); readingsEndUpdate( $hash, 1 ); + } } - $success = 0; + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "ActiveZones", $zonecount); + readingsEndUpdate( $hash, 1 ); } return undef; } -sub TadoAPI_GetGeo(@){ - my ($hash) = @_; +sub TadoAPI_GetGeoById(@){ + my ($hash, $mobileID) = @_; my $name = $hash->{NAME}; my $homeID = $attr{$name}{homeID}; - my $mobileID = $attr{$name}{mobileID}; my $URL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; my $setting = 0; - - Log3 $name, 3, "TadoAPI $name" . ": " . "homeID: $homeID" if $debug; - Log3 $name, 3, "TadoAPI $name" . ": " . "mobID: $mobileID" if $debug; - Log3 $name, 3, "TadoAPI $name" . ": " . "query-URL: $URL" if $debug; - - if($apiStatus == 1){ - TadoAPI_Connect($hash); + og3 $name, 3, "TadoAPI $name" . ": " . "query-URL: $URL" if $debug; + TadoAPI_Connect($hash); - $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); - $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); - $UserAgent->default_headers($header); + if($apiStatus == 1){ + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); - $Request = GET($URL); - $Response = $UserAgent->request($Request); + my $req = GET($URL); + my $res = $ua->request($req); - if($Response->is_success){ - my $ResponseData = decode_json($Response->content); + if($res->is_success){ + my $ResponseData = decode_json($res->content); $setting = $ResponseData->{'geoTrackingEnabled'}; - #print "\n Retriving State:\n" if $debug; - #print $Response->content."\n\n" if $debug; Log3 $name, 3, "TadoAPI $name" . ": " . "Actual geo setting for $mobileID is: $setting" if $debug; readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "Geolocation", $setting); + readingsBulkUpdate($hash, "Geolocation_" . $mobileID, $setting); readingsEndUpdate( $hash, 1 ); return $setting; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error in TadoAPI_GetGeo()]"; - Log3 $name, 3, "TadoAPI $name" . ": " . "Error: ". $Response->status_line if $debug; + Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error in TadoAPI_GetGeoById( $mobileID )]"; + Log3 $name, 3, "TadoAPI $name" . ": " . "Error: ". $res->status_line if $debug; } } } @@ -760,7 +708,7 @@ sub TadoAPI_GetZoneInfo(@) { if($res->is_success){ Log3 $name, 3, "TadoAPI $name" . ": " . "Home Info:\n" . $res->content . "\n"; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $res->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneInfo: [Authentication Error]". $res->status_line; } # TadoDevicesInfo $URL = $QueryURL.qq{/$homeID/zones}; @@ -772,7 +720,7 @@ sub TadoAPI_GetZoneInfo(@) { Log3 $name, 3, "TadoAPI $name" . ": " . "Tado Devices:\n" . $res->content . "\n"; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $res->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneInfo: [Authentication Error]". $res->status_line; } # Mobileinfo $URL = $QueryURL . qq{/$homeID/mobileDevices}; @@ -783,10 +731,22 @@ sub TadoAPI_GetZoneInfo(@) { if($res->is_success){ Log3 $name, 3, "TadoAPI $name" . ": " . "Mobile Devices:\n" . $res->content . "\n"; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $res->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneInfo: [Authentication Error]". $res->status_line; } + my $mobileID = $attr{$name}{mobileID}; + $URL=$QueryURL.qq{/$homeID/mobileDevices/$mobileID/settings}; + $req = GET($URL); + $res = $UserAgent->request($req); + + if($res->is_success){ + Log3 $name, 3, "TadoAPI $name" . ": " . "Mobile Device $mobileID :\n" . $res->content . "\n"; + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneInfo: [Authentication Error]". $res->status_line; + } + +# zones for (my $i=1; $i <= TadoAPI_GetZoneCount($hash); $i++) { $URL=$QueryURL.qq{/$homeID/zones/$i/state}; @@ -798,47 +758,13 @@ sub TadoAPI_GetZoneInfo(@) { my $zoneName = TadoAPI_GetZoneNameById($hash, $i); Log3 $name, 3, "TadoAPI $name" . ": " . "Zone $i ($zoneName) Status:\n" . $res->content; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $res->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneInfo [Authentication Error]". $res->status_line; } } } return undef; } -sub TadoAPI_GetHomeMode(@) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $homeID = $attr{$name}{homeID}; - my $URL=$QueryURL.qq{/$homeID/zones/1/state}; - my $tadoMode = 0; - - if($apiStatus == 1){ - TadoAPI_Connect($hash); - - $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); - $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); - $UserAgent->default_headers($header); - - $Request = GET($URL); - $Response = $UserAgent->request($Request); - - if($Response->is_success){ - my $ResponseData = decode_json($Response->content); - $tadoMode = $ResponseData->{'tadoMode'}; - #debug - #{Log 3, ("Retriving Zone 1 State:") if $verbose}; - #print $Response->content."\n\n" if $verbose; - Log3 $name, 3, "TadoAPI $name" . ": " . "Actual tado mode is: $tadoMode" if $debug; - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "HomeMode", $tadoMode); - readingsEndUpdate( $hash, 1 ); - }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]: " . $Response->status_line if $debug; - } - } - return 0; -} - sub TadoAPI_SetGeo(@){ my ($hash, $geo) = @_; my $name = $hash->{NAME}; @@ -881,6 +807,200 @@ sub TadoAPI_SetGeo(@){ } } +sub TadoAPI_SetGeoById(@){ + my ($hash, $mobID, $geo) = @_; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL=$QueryURL.qq{/$homeID/mobileDevices/$mobID/settings}; + + TadoAPI_Connect($hash); + + if($apiStatus == 1){ + + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); + + my $req = HTTP::Request->new('PUT',$URL); + $req->content_type('application/json'); + + if($geo){ + $req->content('{"geoTrackingEnabled":"true"}'); + }else{ + $req->content('{"geoTrackingEnabled":"false"}'); + } + + my $res = $ua->request($req); + + if($res->is_success){ + print "\n Retriving State:\n" if $debug; + Log3 $name, 3, "TadoAPI $name" . ": " . "Retriving state:\n" . $res->content if $debug; + Log3 $name, 3, "TadoAPI $name" . ": " . "Set geo setting for $mobID to: $geo"; + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "Error in setGeo()"; + Log3 $name, 3, "TadoAPI $name" . ": " . "Status: " . $res->status_line if $debug; + } + } +} + +####################################################################################################### +# API Subs +########### + +sub TadoAPI_RequestHome(@) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $URL=qq{https://my.tado.com/api/v2/me}; + + if($apiStatus == 1){ + TadoAPI_Connect($hash); + + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); + + my $req = GET($URL); + my $res = $ua->request($req); + + if($res->is_success){ + Log3 $name, 3, "TadoAPI $name" . ": " . "TadoAPI_RequestHome:\n" . $res->content . "\n" if $debug; + my $data = $res->content; + # validate response from api + eval { decode_json($data) }; + if ($@) + { + Log3 $name, 3, "TadoAPI $name" . ": " . "TadoAPI_RequestHome: decode_json failed, invalid json. error:$@\n"; + }else{ + # returns json + return $data; + } + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "TadoAPI_RequestHome: [Authentication Error]". $res->status_line; + } + } + return undef; +} + + +sub TadoAPI_RequestTadoDevices(@) { + # returns array with zonenames and zone devices + my ($hash) = @_; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL=$QueryURL.qq{/$homeID/zones}; + + if($apiStatus == 1){ + TadoAPI_Connect($hash); + + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); + + my $req = GET($URL); + my $res = $ua->request($req); + + if($res->is_success){ + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestTadoDevices:\n" . $res->content . "\n" if $debug; + my $data = $res->content; + # validate response from api + my $decoded_data = eval { decode_json($data) }; + if ($@) + { + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestTadoDevices: decode_json failed, invalid json. error:$@\n"; + }else{ + my @devices = (); + foreach my $dev (@$decoded_data){ + push @devices, $dev; + } + return @devices; + } + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestTadoDevices: [Authentication Error]". $res->status_line; + } + } +return undef; +} + +sub TadoAPI_RequestZones(@) { + # returns array with state of all zones + my ($hash) = @_; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + + if($apiStatus == 1){ + TadoAPI_Connect($hash); + + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); + + my @zones = (); + + for (my $i=1; $i <= TadoAPI_GetZoneCount($hash); $i++) { + my $URL=$QueryURL.qq{/$homeID/zones/$i/state}; + + my $req = GET($URL); + my $res = $UserAgent->request($req); + + if($res->is_success){ + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestZones: Zone $i Status:\n" . $res->content if $debug; + my $data = $res->content; + # validate response from api + my $decoded_data = eval { decode_json($data) }; + if ($@) + { + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestZones: Zone $i decode_json failed, invalid json. error:$@\n"; + }else{ + # returns a json object with zone i + push @zones, $decoded_data; + } + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestZones: [Authentication Error]". $res->status_line; + } + } + return @zones; + } +return undef; +} + +sub TadoAPI_RequestMobileDevices(@) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL=$QueryURL . qq{/$homeID/mobileDevices}; + + if($apiStatus == 1){ + TadoAPI_Connect($hash); + my @devices = (); + + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); + + my $req = GET($URL); + my $res = $ua->request($req); + + if($res->is_success){ + my $data = $res->content; + # validate response from api + my $decoded_data = eval { decode_json($data) }; + if ($@) + { + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestMobileDevice: decode_json failed, invalid json. error:$@\n"; + }else{ + foreach my $item( @$decoded_data ) { + push @devices, $item; + } + } + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "RequestMobileDevices: [Authentication Error]". $res->status_line; + } + return @devices; + } +return undef; +} + + ###################################### ############ Helpers ################# ###################################### @@ -905,7 +1025,7 @@ sub TadoAPI_GetZoneCount(@) { } return $zonecount; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneCount: [Authentication Error]". $Response->status_line; } return undef; } @@ -925,10 +1045,39 @@ sub TadoAPI_GetZoneNameById(@) { if($Response->is_success){ my $ResponseData = decode_json($Response->content); - my $zoneName = @$ResponseData[$zoneID - 1]->{'name'}; + my $zoneName = encode("UTF-8", @$ResponseData[$zoneID - 1]->{'name'}); return $zoneName; }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneNameById: [Authentication Error]". $Response->status_line; + } +return undef; +} + +# todo delete this +sub TadoAPI_GetZoneDevicesById(@) { + my ($hash, $zoneID) = @_; + my $name = $hash->{NAME}; + my $homeID = $attr{$name}{homeID}; + my $URL=$QueryURL.qq{/$homeID/zones}; + + $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $UserAgent->default_headers($header); + + $Request = GET($URL); + $Response = $UserAgent->request($Request); + + if($Response->is_success){ + my $devcount = 0; + my $ResponseData = decode_json($Response->content); + my $zoneDeviceList = @$ResponseData[$zoneID - 1]->{'devices'}; + + foreach my $item( @$zoneDeviceList ) { + $devcount++; + } + return ($devcount, $zoneDeviceList); + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneDevicesById: [Authentication Error]". $Response->status_line; } return undef; } @@ -955,7 +1104,7 @@ sub TadoAPI_GetZoneTemperatureById(@){ Log3 $name, 3, "TadoAPI $name" . ": " . "Temperature: $temperature Humidity: $humidity\n" if $debug; return ($temperature, $humidity); }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneTemperatureById: [Authentication Error]". $Response->status_line; } return undef; } @@ -971,39 +1120,109 @@ sub TadoAPI_GetZoneReadingsById(@){ my $currentHeatingPower = 0; my $overlay = 0; - $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); - $UserAgent = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); - $UserAgent->default_headers($header); + my $header = HTTP::Headers->new("Content-Type"=>"application/json;charset=UTF-8","Authorization" => "$TokenData->{'token_type'} $TokenData->{'access_token'}"); + my $ua = LWP::UserAgent::Paranoid->new(ssl_opts => { verify_hostname => 1 },protocols_allowed => ['https','http'],request_timeout => 5,); + $ua->default_headers($header); - $Request = GET($URL); - $Response = $UserAgent->request($Request); + my $req = GET($URL); + my $res = $ua->request($req); - if($Response->is_success){ - my $ResponseData = decode_json($Response->content); - $temperature = $ResponseData->{'sensorDataPoints'}->{'insideTemperature'}->{'celsius'}; - $humidity = $ResponseData->{'sensorDataPoints'}->{'humidity'}->{'percentage'}; - $desiredTemp = $ResponseData->{'setting'}->{'temperature'}->{'celsius'}; - $currentHeatingPower = $ResponseData->{'activityDataPoints'}->{'heatingPower'}->{'percentage'}; - $overlay = $ResponseData->{'overlayType'}; - if (!defined $overlay) {$overlay = "no overlay"}; + if($res->is_success){ + my $ResponseData = decode_json($res->content); + $temperature = $ResponseData->{'sensorDataPoints'}->{'insideTemperature'}->{'celsius'}; + $humidity = $ResponseData->{'sensorDataPoints'}->{'humidity'}->{'percentage'}; + $desiredTemp = $ResponseData->{'setting'}->{'temperature'}->{'celsius'}; + $currentHeatingPower = $ResponseData->{'activityDataPoints'}->{'heatingPower'}->{'percentage'}; + $overlay = $ResponseData->{'overlayType'}; + if (!defined $overlay) {$overlay = "no overlay"}; - my $zoneName = TadoAPI_GetZoneNameById($hash, $zoneID); + my $zoneName = TadoAPI_GetZoneNameById($hash, $zoneID); - 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 ); + 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 ($temperature, $humidity, $desiredTemp, $currentHeatingPower, $overlay ); - }else{ - Log3 $name, 3, "TadoAPI $name" . ": " . "[Authentication Error]". $Response->status_line; - } + return ($temperature, $humidity, $desiredTemp, $currentHeatingPower, $overlay ); + }else{ + Log3 $name, 3, "TadoAPI $name" . ": " . "GetZoneReadingsById: [Authentication Error]". $Response->status_line; + } return undef; } +###################################################### +# storePW & readPW Code geklaut aus 96_SIP.pm :) +###################################################### +sub TadoAPI_storePassword($$) +{ + my ($name, $password) = @_; + my $index = "TadoAPI_".$name."_passwd"; + my $key = getUniqueId().$index; + my $e_pwd = ""; + + if (eval "use Digest::MD5;1") + { + $key = Digest::MD5::md5_hex(unpack "H*", $key); + $key .= Digest::MD5::md5_hex($key); + } + + for my $char (split //, $password) + { + my $encode=chop($key); + $e_pwd.=sprintf("%.2x",ord($char)^ord($encode)); + $key=$encode.$key; + } + + my $error = setKeyValue($index, $e_pwd); + return "error while saving TadoAPI password : $error" if(defined($error)); + return "TadoAPI password successfully saved in FhemUtils/uniqueID Key $index"; +} + +sub TadoAPI_readPassword($) +{ + my ($name) = @_; + my $index = "TadoAPI_".$name."_passwd"; + my $key = getUniqueId().$index; + + my ($password, $error); + + #Log3 $name,5,"$name, read user password from FhemUtils/uniqueID Key $key"; + ($error, $password) = getKeyValue($index); + + if ( defined($error) ) + { + Log3 $name,3, "$name, cant't read Tado password from FhemUtils/uniqueID: $error"; + return undef; + } + + if ( defined($password) ) + { + if (eval "use Digest::MD5;1") + { + $key = Digest::MD5::md5_hex(unpack "H*", $key); + $key .= Digest::MD5::md5_hex($key); + } + + my $dec_pwd = ''; + + for my $char (map { pack('C', hex($_)) } ($password =~ /(..)/g)) + { + my $decode=chop($key); + $dec_pwd.=chr(ord($char)^ord($decode)); + $key=$decode.$key; + } + return $dec_pwd; + } + else + { + Log3 $name,3,"$name, no Tado password found in FhemUtils/uniqueID"; + return undef; + } +} + 1; # Beginn der Commandref