python-tado 0.18.12__tar.gz → 0.18.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_tado-0.18.12/python_tado.egg-info → python_tado-0.18.14}/PKG-INFO +1 -1
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/http.py +42 -38
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/interface/api/hops_tado.py +59 -9
- {python_tado-0.18.12 → python_tado-0.18.14}/pyproject.toml +1 -1
- {python_tado-0.18.12 → python_tado-0.18.14/python_tado.egg-info}/PKG-INFO +1 -1
- {python_tado-0.18.12 → python_tado-0.18.14}/AUTHORS +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/LICENSE +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/__init__.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/__main__.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/const.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/exceptions.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/interface/__init__.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/interface/api/__init__.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/interface/api/my_tado.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/interface/interface.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/logger.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/zone/__init__.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/zone/hops_zone.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/PyTado/zone/my_zone.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/README.md +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/SOURCES.txt +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/dependency_links.txt +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/entry_points.txt +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/not-zip-safe +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/requires.txt +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/python_tado.egg-info/top_level.txt +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/setup.cfg +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/tests/test_hops_zone.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/tests/test_http.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/tests/test_my_tado.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/tests/test_my_zone.py +0 -0
- {python_tado-0.18.12 → python_tado-0.18.14}/tests/test_tado_interface.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-tado
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.14
|
|
4
4
|
Summary: PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon, FilBr, nikilase, albertomontesg, Moritz-Schmidt, palazzem
|
|
5
5
|
Author-email: Chris Jewell <chrism0dwk@gmail.com>, "w.malgadey" <w.malgadey@gmail.com>, FilBr <filippo.barba@protonmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -7,11 +7,13 @@ import json
|
|
|
7
7
|
import logging
|
|
8
8
|
import os
|
|
9
9
|
import pprint
|
|
10
|
+
import threading
|
|
10
11
|
import time
|
|
11
12
|
from datetime import datetime, timedelta, timezone
|
|
12
13
|
from json import dump as json_dump
|
|
13
14
|
from json import load as json_load
|
|
14
15
|
from pathlib import Path
|
|
16
|
+
from threading import Lock
|
|
15
17
|
from typing import Any
|
|
16
18
|
from urllib.parse import urlencode
|
|
17
19
|
|
|
@@ -146,6 +148,8 @@ _DEFAULT_RETRIES = 5
|
|
|
146
148
|
class Http:
|
|
147
149
|
"""API Request Class"""
|
|
148
150
|
|
|
151
|
+
_lock: threading.Lock = Lock()
|
|
152
|
+
|
|
149
153
|
def __init__(
|
|
150
154
|
self,
|
|
151
155
|
token_file_path: str | None = None,
|
|
@@ -396,51 +400,51 @@ class Http:
|
|
|
396
400
|
TadoWrongCredentialsException: If the token refresh fails due to invalid credentials
|
|
397
401
|
and force_refresh is False.
|
|
398
402
|
"""
|
|
403
|
+
with self._lock:
|
|
404
|
+
if self._refresh_at >= datetime.now(timezone.utc) and not force_refresh:
|
|
405
|
+
return True
|
|
406
|
+
|
|
407
|
+
url = "https://login.tado.com/oauth2/token"
|
|
408
|
+
data = {
|
|
409
|
+
"client_id": CLIENT_ID_DEVICE,
|
|
410
|
+
"grant_type": "refresh_token",
|
|
411
|
+
"refresh_token": refresh_token or self._token_refresh,
|
|
412
|
+
}
|
|
413
|
+
self._session.close()
|
|
414
|
+
self._session = self._create_session()
|
|
399
415
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
416
|
+
try:
|
|
417
|
+
response = self._session.request(
|
|
418
|
+
"post",
|
|
419
|
+
url,
|
|
420
|
+
params=data,
|
|
421
|
+
timeout=_DEFAULT_TIMEOUT,
|
|
422
|
+
data=json.dumps({}).encode("utf8"),
|
|
423
|
+
headers={
|
|
424
|
+
"Content-Type": "application/json",
|
|
425
|
+
"Referer": "https://app.tado.com/",
|
|
426
|
+
},
|
|
427
|
+
)
|
|
411
428
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
url,
|
|
416
|
-
params=data,
|
|
417
|
-
timeout=_DEFAULT_TIMEOUT,
|
|
418
|
-
data=json.dumps({}).encode("utf8"),
|
|
419
|
-
headers={
|
|
420
|
-
"Content-Type": "application/json",
|
|
421
|
-
"Referer": "https://app.tado.com/",
|
|
422
|
-
},
|
|
423
|
-
)
|
|
429
|
+
except requests.exceptions.ConnectionError as e:
|
|
430
|
+
_LOGGER.error("Connection error: %s", e)
|
|
431
|
+
raise TadoException(e) from e
|
|
424
432
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
433
|
+
if response.status_code != 200:
|
|
434
|
+
if force_refresh:
|
|
435
|
+
_LOGGER.error(
|
|
436
|
+
"Failed to refresh token, probably wrong credentials. Status code: %s",
|
|
437
|
+
response.status_code,
|
|
438
|
+
)
|
|
439
|
+
return False
|
|
428
440
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
_LOGGER.error(
|
|
432
|
-
"Failed to refresh token, probably wrong credentials. Status code: %s",
|
|
433
|
-
response.status_code,
|
|
441
|
+
raise TadoWrongCredentialsException(
|
|
442
|
+
f"Failed to refresh token, probably wrong credentials. Status code: {response.status_code}"
|
|
434
443
|
)
|
|
435
|
-
return False
|
|
436
444
|
|
|
437
|
-
|
|
438
|
-
f"Failed to refresh token, probably wrong credentials. Status code: {response.status_code}"
|
|
439
|
-
)
|
|
440
|
-
|
|
441
|
-
self._set_oauth_header(response.json())
|
|
445
|
+
self._set_oauth_header(response.json())
|
|
442
446
|
|
|
443
|
-
|
|
447
|
+
return True
|
|
444
448
|
|
|
445
449
|
def _save_token(self):
|
|
446
450
|
"""Save the refresh token to a file."""
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
PyTado interface implementation for hops.tado.com (Tado X).
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import functools
|
|
5
6
|
import logging
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -11,6 +12,18 @@ from ...logger import Logger
|
|
|
11
12
|
from ...zone import TadoXZone, TadoZone
|
|
12
13
|
from .my_tado import Tado, Timetable
|
|
13
14
|
|
|
15
|
+
|
|
16
|
+
def not_supported(reason):
|
|
17
|
+
def decorator(func):
|
|
18
|
+
@functools.wraps(func)
|
|
19
|
+
def wrapper(*args, **kwargs):
|
|
20
|
+
raise TadoNotSupportedException(f"{func.__name__} is not supported: {reason}")
|
|
21
|
+
|
|
22
|
+
return wrapper
|
|
23
|
+
|
|
24
|
+
return decorator
|
|
25
|
+
|
|
26
|
+
|
|
14
27
|
_LOGGER = Logger(__name__)
|
|
15
28
|
|
|
16
29
|
|
|
@@ -60,9 +73,13 @@ class TadoX(Tado):
|
|
|
60
73
|
devices = [device for room in rooms for device in room["devices"]]
|
|
61
74
|
|
|
62
75
|
for device in devices:
|
|
76
|
+
serial_number = device.get("serialNo", device.get("serialNumber"))
|
|
77
|
+
if not serial_number:
|
|
78
|
+
continue
|
|
79
|
+
|
|
63
80
|
request = TadoXRequest()
|
|
64
81
|
request.domain = Domain.DEVICES
|
|
65
|
-
request.device =
|
|
82
|
+
request.device = serial_number
|
|
66
83
|
device.update(self._http.request(request))
|
|
67
84
|
|
|
68
85
|
if "otherDevices" in rooms_and_devices:
|
|
@@ -108,12 +125,12 @@ class TadoX(Tado):
|
|
|
108
125
|
|
|
109
126
|
return data
|
|
110
127
|
|
|
128
|
+
@not_supported("This method is not currently supported by the Tado X API")
|
|
111
129
|
def get_capabilities(self, zone):
|
|
112
130
|
"""
|
|
113
131
|
Gets current capabilities of zone.
|
|
114
132
|
"""
|
|
115
|
-
|
|
116
|
-
raise TadoNotSupportedException("This method is not currently supported by the Tado X API")
|
|
133
|
+
pass
|
|
117
134
|
|
|
118
135
|
def get_climate(self, zone):
|
|
119
136
|
"""
|
|
@@ -126,6 +143,7 @@ class TadoX(Tado):
|
|
|
126
143
|
"humidity": data["humidity"]["percentage"],
|
|
127
144
|
}
|
|
128
145
|
|
|
146
|
+
@not_supported("Tado X API only support seven days timetable")
|
|
129
147
|
def set_timetable(self, zone: int, timetable: Timetable) -> None:
|
|
130
148
|
"""
|
|
131
149
|
Set the Timetable type currently active
|
|
@@ -133,8 +151,7 @@ class TadoX(Tado):
|
|
|
133
151
|
id = 1 : THREE_DAY (MONDAY_TO_FRIDAY, SATURDAY, SUNDAY)
|
|
134
152
|
id = 3 : SEVEN_DAY (MONDAY, TUESDAY, WEDNESDAY ...)
|
|
135
153
|
"""
|
|
136
|
-
|
|
137
|
-
raise TadoNotSupportedException("Tado X API only support seven days timetable")
|
|
154
|
+
pass
|
|
138
155
|
|
|
139
156
|
def get_schedule(self, zone: int, timetable: Timetable, day=None) -> dict[str, Any]:
|
|
140
157
|
"""
|
|
@@ -249,12 +266,12 @@ class TadoX(Tado):
|
|
|
249
266
|
|
|
250
267
|
return self._http.request(request)
|
|
251
268
|
|
|
269
|
+
@not_supported("Concept of zones is not available by Tado X API, they use rooms")
|
|
252
270
|
def get_zone_overlay_default(self, zone: int):
|
|
253
271
|
"""
|
|
254
272
|
Get current overlay default settings for zone.
|
|
255
273
|
"""
|
|
256
|
-
|
|
257
|
-
raise TadoNotSupportedException("Concept of zones is not available by Tado X API, they use rooms")
|
|
274
|
+
pass
|
|
258
275
|
|
|
259
276
|
def get_open_window_detected(self, zone):
|
|
260
277
|
"""
|
|
@@ -273,15 +290,21 @@ class TadoX(Tado):
|
|
|
273
290
|
Sets the window in zone to open
|
|
274
291
|
Note: This can only be set if an open window was detected in this zone
|
|
275
292
|
"""
|
|
293
|
+
request = TadoXRequest()
|
|
294
|
+
request.command = f"rooms/{zone}/openWindow"
|
|
295
|
+
request.action = Action.SET
|
|
276
296
|
|
|
277
|
-
|
|
297
|
+
return self._http.request(request)
|
|
278
298
|
|
|
279
299
|
def reset_open_window(self, zone):
|
|
280
300
|
"""
|
|
281
301
|
Sets the window in zone to closed
|
|
282
302
|
"""
|
|
303
|
+
request = TadoXRequest()
|
|
304
|
+
request.command = f"rooms/{zone}/openWindow"
|
|
305
|
+
request.action = Action.RESET
|
|
283
306
|
|
|
284
|
-
|
|
307
|
+
return self._http.request(request)
|
|
285
308
|
|
|
286
309
|
def get_device_info(self, device_id, cmd=""):
|
|
287
310
|
"""
|
|
@@ -324,3 +347,30 @@ class TadoX(Tado):
|
|
|
324
347
|
request.payload = {"childLockEnabled": child_lock}
|
|
325
348
|
|
|
326
349
|
self._http.request(request)
|
|
350
|
+
|
|
351
|
+
def set_flow_temperature_optimization(self, max_flow_temperature: float):
|
|
352
|
+
"""
|
|
353
|
+
Set the flow temperature optimization.
|
|
354
|
+
|
|
355
|
+
max_flow_temperature: float, the maximum flow temperature in Celsius
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
request = TadoXRequest()
|
|
359
|
+
request.action = Action.CHANGE
|
|
360
|
+
request.domain = Domain.HOME
|
|
361
|
+
request.command = "settings/flowTemperatureOptimization"
|
|
362
|
+
request.payload = {"maxFlowTemperature": max_flow_temperature}
|
|
363
|
+
|
|
364
|
+
return self._http.request(request)
|
|
365
|
+
|
|
366
|
+
def get_flow_temperature_optimization(self):
|
|
367
|
+
"""
|
|
368
|
+
Get the current flow temperature optimization
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
request = TadoXRequest()
|
|
372
|
+
request.action = Action.GET
|
|
373
|
+
request.domain = Domain.HOME
|
|
374
|
+
request.command = "settings/flowTemperatureOptimization"
|
|
375
|
+
|
|
376
|
+
return self._http.request(request)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-tado"
|
|
7
|
-
version = "0.18.
|
|
7
|
+
version = "0.18.14"
|
|
8
8
|
description = "PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon, FilBr, nikilase, albertomontesg, Moritz-Schmidt, palazzem"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Chris Jewell", email = "chrism0dwk@gmail.com" },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-tado
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.14
|
|
4
4
|
Summary: PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon, FilBr, nikilase, albertomontesg, Moritz-Schmidt, palazzem
|
|
5
5
|
Author-email: Chris Jewell <chrism0dwk@gmail.com>, "w.malgadey" <w.malgadey@gmail.com>, FilBr <filippo.barba@protonmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|