Skip to content

Commit

Permalink
pylint refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolfgang Malgadey committed Jan 8, 2017
1 parent 99ff938 commit 2b28b32
Showing 1 changed file with 89 additions and 48 deletions.
137 changes: 89 additions & 48 deletions PyTado/interface.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import urllib.request, urllib.parse, urllib.error
"""
PyTado interface implementation for mytado.com
"""

from http.cookiejar import CookieJar
import json
import datetime
import urllib.request
import urllib.parse
import urllib.error

import json, datetime
from http.cookiejar import CookieJar


class Tado:
"""Interacts with a Tado thermostat via public API.
Example usage: t = Tado('me@somewhere.com', 'mypasswd')
t.getClimate(1) # Get climate, zone 1.
"""

# Instance-wide constant info
headers = { 'Referer' : 'https://my.tado.com/' }
headers = {'Referer' : 'https://my.tado.com/'}
api2url = 'https://my.tado.com/api/v2/homes/'
mobi2url = 'https://my.tado.com/mobile/1.9/'
refresh_token = ''
Expand All @@ -20,7 +27,9 @@ class Tado:

# 'Private' methods for use in class, Tado mobile API V1.9.
def _mobile_apiCall(self, cmd):
self._refreshToken()
# pylint: disable=C0103

self._refresh_token()

url = '%s%s' % (self.mobi2url, cmd)
req = urllib.request.Request(url, headers=self.headers)
Expand All @@ -31,7 +40,9 @@ def _mobile_apiCall(self, cmd):

# 'Private' methods for use in class, Tado API V2.
def _apiCall(self, cmd, method="GET", data=None, plain=False):
self._refreshToken()
# pylint: disable=C0103

self._refresh_token()

headers = self.headers

Expand All @@ -41,13 +52,13 @@ def _apiCall(self, cmd, method="GET", data=None, plain=False):
else:
headers['Content-Type'] = 'application/json;charset=UTF-8'
headers['Mime-Type'] = 'application/json;charset=UTF-8'
data=json.dumps(data).encode('utf8')
data = json.dumps(data).encode('utf8')

url = '%s%i/%s' % (self.api2url, self.id, cmd)
req = urllib.request.Request(url,
headers=headers,
method=method,
data=data)
req = urllib.request.Request(url,
headers=headers,
method=method,
data=data)

response = self.opener.open(req)
str_response = response.read().decode('utf-8')
Expand All @@ -59,164 +70,194 @@ def _apiCall(self, cmd, method="GET", data=None, plain=False):
return data

def _setOAuthHeader(self, data):
# pylint: disable=C0103

access_token = data['access_token']
expires_in = float(data['expires_in'])
refresh_token = data['refresh_token']

self.refresh_token = refresh_token
self.refresh_at = datetime.datetime.now()
self.refresh_at = self.refresh_at + datetime.timedelta(seconds = expires_in)
self.refresh_at = self.refresh_at + datetime.timedelta(seconds=expires_in)

# we substract 30 seconds from the correct refresh time
# then we have a 30 seconds timespan to get a new refresh_token
self.refresh_at = self.refresh_at + datetime.timedelta(seconds = -30)
self.refresh_at = self.refresh_at + datetime.timedelta(seconds=-30)

self.headers['Authorization'] = 'Bearer ' + access_token

def _refreshToken(self):
def _refresh_token(self):
if self.refresh_at >= datetime.datetime.now():
return False

url='https://my.tado.com/oauth/token'
data = { 'client_id' : 'tado-webapp',
'grant_type' : 'refresh_token',
'scope' : 'home.user',
'refresh_token' : self.refresh_token }
url = 'https://my.tado.com/oauth/token'
data = {'client_id' : 'tado-webapp',
'grant_type' : 'refresh_token',
'scope' : 'home.user',
'refresh_token' : self.refresh_token}
# pylint: disable=R0204
data = urllib.parse.urlencode(data)
url = url + '?' + data
req = urllib.request.Request(url, data=json.dumps({}).encode('utf8'), method='POST',
headers={'Content-Type': 'application/json', 'Referer' : 'https://my.tado.com/'})

headers={'Content-Type': 'application/json',
'Referer' : 'https://my.tado.com/'})

response = self.opener.open(req)
str_response = response.read().decode('utf-8')

self._setOAuthHeader(json.loads(str_response))
return response

def _loginV2(self, username, password):
# pylint: disable=C0103

headers = self.headers
headers['Content-Type'] = 'application/json'

url='https://my.tado.com/oauth/token'
data = { 'client_id' : 'tado-webapp',
'grant_type' : 'password',
'password' : password,
'scope' : 'home.user',
'username' : username }
url = 'https://my.tado.com/oauth/token'
data = {'client_id' : 'tado-webapp',
'grant_type' : 'password',
'password' : password,
'scope' : 'home.user',
'username' : username}
# pylint: disable=R0204
data = urllib.parse.urlencode(data)
url = url + '?' + data
req = urllib.request.Request(url, data=json.dumps({}).encode('utf8'), method='POST',
headers={'Content-Type': 'application/json', 'Referer' : 'https://my.tado.com/'})

headers={'Content-Type': 'application/json',
'Referer' : 'https://my.tado.com/'})

response = self.opener.open(req)
str_response = response.read().decode('utf-8')

self._setOAuthHeader(json.loads(str_response))
return response

# Public interface
def getMe(self):
"""Gets home information."""
# pylint: disable=C0103

url = 'https://my.tado.com/api/v2/me'
req = urllib.request.Request(url, headers=self.headers)
response = self.opener.open(req)
str_response = response.read().decode('utf-8')
data = json.loads(str_response)
return data

def getDevices(self):
"""Gets device information."""
# pylint: disable=C0103

cmd = 'devices'
data = self._apiCall(cmd)
return data

def getZones(self):
"""Gets zones information."""
# pylint: disable=C0103

cmd = 'zones'
data = self._apiCall(cmd)
return data

def getState(self, zone):
"""Gets current state of Zone zone."""
# pylint: disable=C0103

cmd = 'zones/%i/state' % zone
data = self._apiCall(cmd)
return data

def getCapabilities(self, zone):
"""Gets current capabilities of Zone zone."""
# pylint: disable=C0103

cmd = 'zones/%i/capabilities' % zone
data = self._apiCall(cmd)
return data

def getClimate(self, zone):
"""Gets temp (centigrade) and humidity (% RH) for Zone zone."""
cmd = 'zones/%i/state' % zone
# pylint: disable=C0103

data = self.getState(zone)['sensorDataPoints']
return { 'temperature' : data['insideTemperature']['celsius'],
'humidity' : data['humidity']['percentage'] }
return {'temperature' : data['insideTemperature']['celsius'],
'humidity' : data['humidity']['percentage']}

def getWeather(self):
"""Gets outside weather data"""
# pylint: disable=C0103

cmd = 'weather'
data = self._apiCall(cmd)
return data

def getAppUsers(self):
"""Gets getAppUsers data"""
# pylint: disable=C0103

cmd = 'getAppUsers'
data = self._mobile_apiCall(cmd)
return data

def getAppUsersRelativePositions(self):
"""Gets getAppUsersRelativePositions data"""
# pylint: disable=C0103

cmd = 'getAppUsersRelativePositions'
data = self._mobile_apiCall(cmd)
return data

def resetZoneOverlay(self, zone):
"""Delete current overlay"""
# pylint: disable=C0103

cmd = 'zones/%i/overlay' % zone
data = self._apiCall(cmd, "DELETE", {}, True)
return data

def setZoneOverlay(self, zone, overlayMode, setTemp=None, duration=None):
"""set current overlay for a zone"""
# pylint: disable=C0103

cmd = 'zones/%i/overlay' % zone

postData = { "setting" : {}, "termination" : {} }
post_data = {
"setting" : {},
"termination" : {}
}

if setTemp is None:
postData["setting"] = {
post_data["setting"] = {
"type":"HEATING",
"power":"OFF"
}
else:
postData["setting"] = {
post_data["setting"] = {
"type":"HEATING",
"power":"ON",
"temperature":{
"celsius": setTemp
}
}

postData["termination"] = {
"type":overlayMode
#{"type":"TIMER","durationInSeconds":900}
#{"type":"MANUAL"}
#{"type":"TADO_MODE"}
}
post_data["termination"] = {"type" : overlayMode}

if duration is not None:
postData["termination"]["durationInSeconds"] = duration
post_data["termination"]["durationInSeconds"] = duration

data = self._apiCall(cmd, "PUT", postData, True)
data = self._apiCall(cmd, "PUT", post_data, True)
return data

# Ctor
def __init__(self, username, password):
"""Performs login and save session cookie."""
# HTTPS Interface

# pylint: disable=C0103
cj = CookieJar()

self.opener = urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(cj),
urllib.request.HTTPSHandler())
Expand Down

0 comments on commit 2b28b32

Please sign in to comment.