python-tado 0.17.6__tar.gz → 0.17.7__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.17.6 → python_tado-0.17.7}/AUTHORS +1 -0
- {python_tado-0.17.6/python_tado.egg-info → python_tado-0.17.7}/PKG-INFO +1 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/const.py +1 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/exceptions.py +3 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/http/__init__.py +1 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/http/http.py +32 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/interface.py +157 -35
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/zone.py +39 -11
- {python_tado-0.17.6 → python_tado-0.17.7/python_tado.egg-info}/PKG-INFO +1 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/setup.py +1 -1
- {python_tado-0.17.6 → python_tado-0.17.7}/LICENSE +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/__init__.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/__main__.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/logging/__init__.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/logging/logging.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/polyfill/__init__.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/PyTado/polyfill/strenum.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/README.md +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/SOURCES.txt +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/dependency_links.txt +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/entry_points.txt +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/not-zip-safe +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/requires.txt +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/python_tado.egg-info/top_level.txt +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/setup.cfg +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/tests/test_tado.py +0 -0
- {python_tado-0.17.6 → python_tado-0.17.7}/tests/test_zone.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-tado
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.7
|
|
4
4
|
Summary: PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon
|
|
5
5
|
Home-page: https://github.com/wmalgadey/PyTado
|
|
6
6
|
Author: chrism0dwk, w.malgadey
|
|
@@ -43,7 +43,7 @@ CONST_HORIZONTAL_SWING_RIGHT = "RIGHT"
|
|
|
43
43
|
|
|
44
44
|
# When we change the temperature setting, we need an overlay mode
|
|
45
45
|
CONST_OVERLAY_TADO_MODE = "NEXT_TIME_BLOCK" # wait until tado changes the mode automatic
|
|
46
|
-
CONST_OVERLAY_MANUAL = "MANUAL" # the user has
|
|
46
|
+
CONST_OVERLAY_MANUAL = "MANUAL" # the user has changed the temperature or mode manually
|
|
47
47
|
CONST_OVERLAY_TIMER = "TIMER" # the temperature will be reset after a timespan
|
|
48
48
|
|
|
49
49
|
# Heat always comes first since we get the
|
|
@@ -8,6 +8,9 @@ class TadoException(Exception):
|
|
|
8
8
|
class TadoNotSupportedException(TadoException):
|
|
9
9
|
"""Exception to indicate a requested action is not supported by Tado."""
|
|
10
10
|
|
|
11
|
+
class TadoXNotSupportedException(TadoException):
|
|
12
|
+
"""Exception to indicate a requested action is not supported by the Tado X API."""
|
|
13
|
+
|
|
11
14
|
|
|
12
15
|
class TadoCredentialsException(TadoException):
|
|
13
16
|
"""Exception to indicate something with credentials"""
|
|
@@ -1 +1 @@
|
|
|
1
|
-
from .http import Action, Domain, Endpoint, Http, Mode, TadoRequest
|
|
1
|
+
from .http import Action, Domain, Endpoint, Http, Mode, TadoRequest, TadoXRequest
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import logging
|
|
7
7
|
import pprint
|
|
8
8
|
from datetime import datetime, timedelta
|
|
9
|
+
from typing import Union
|
|
9
10
|
|
|
10
11
|
try:
|
|
11
12
|
from enum import StrEnum, ReprEnum
|
|
@@ -26,6 +27,11 @@ class Endpoint(StrEnum):
|
|
|
26
27
|
MOBILE = "https://my.tado.com/mobile/1.9/"
|
|
27
28
|
EIQ = "https://energy-insights.tado.com/api/"
|
|
28
29
|
|
|
30
|
+
class EndpointX(StrEnum):
|
|
31
|
+
"""Endpoint URL Enum"""
|
|
32
|
+
API = "https://hops.tado.com/"
|
|
33
|
+
EIQ = "https://energy-insights.tado.com/api/"
|
|
34
|
+
|
|
29
35
|
|
|
30
36
|
class Domain(StrEnum):
|
|
31
37
|
"""API Request Domain Enum"""
|
|
@@ -40,6 +46,7 @@ class Action(StrEnum):
|
|
|
40
46
|
SET = "POST"
|
|
41
47
|
RESET = "DELETE"
|
|
42
48
|
CHANGE = "PUT"
|
|
49
|
+
UPDATE = "PATCH"
|
|
43
50
|
|
|
44
51
|
|
|
45
52
|
class Mode(Enum):
|
|
@@ -58,6 +65,16 @@ class TadoRequest:
|
|
|
58
65
|
device = None
|
|
59
66
|
mode = Mode.OBJECT
|
|
60
67
|
|
|
68
|
+
class TadoXRequest:
|
|
69
|
+
"""Data Container"""
|
|
70
|
+
endpoint = EndpointX.API
|
|
71
|
+
command = None
|
|
72
|
+
action = Action.GET
|
|
73
|
+
payload = None
|
|
74
|
+
domain = Domain.HOME
|
|
75
|
+
device = None
|
|
76
|
+
mode = Mode.OBJECT
|
|
77
|
+
|
|
61
78
|
|
|
62
79
|
class TadoResponse:
|
|
63
80
|
"""Unimplemented Response Container
|
|
@@ -83,6 +100,8 @@ class Http:
|
|
|
83
100
|
# Id
|
|
84
101
|
id = 0
|
|
85
102
|
|
|
103
|
+
isX = False
|
|
104
|
+
|
|
86
105
|
__username = None
|
|
87
106
|
__password = None
|
|
88
107
|
|
|
@@ -111,7 +130,7 @@ class Http:
|
|
|
111
130
|
f"Response:\n\tStatusCode: {response_status}\n\tData: {response.json()}"
|
|
112
131
|
)
|
|
113
132
|
|
|
114
|
-
def request(self, request: TadoRequest):
|
|
133
|
+
def request(self, request: Union[TadoRequest, TadoXRequest]):
|
|
115
134
|
"""Request something from the API with a TadoRequest"""
|
|
116
135
|
self.__refresh_token()
|
|
117
136
|
|
|
@@ -250,6 +269,7 @@ class Http:
|
|
|
250
269
|
if response.status_code == 200:
|
|
251
270
|
self.__set_oauth_header(response.json())
|
|
252
271
|
self.__get_id()
|
|
272
|
+
self.__check_x()
|
|
253
273
|
|
|
254
274
|
def __get_id(self):
|
|
255
275
|
if self.id == 0:
|
|
@@ -257,3 +277,14 @@ class Http:
|
|
|
257
277
|
request.action = Action.GET
|
|
258
278
|
request.domain = Domain.ME
|
|
259
279
|
self.id = self.request(request)['homes'][0]['id']
|
|
280
|
+
|
|
281
|
+
def __check_x(self):
|
|
282
|
+
# get home info
|
|
283
|
+
request = TadoRequest()
|
|
284
|
+
request.action = Action.GET
|
|
285
|
+
request.domain = Domain.HOME
|
|
286
|
+
request.command = ""
|
|
287
|
+
home = self.request(request)
|
|
288
|
+
if 'generation' in home and home['generation'] == 'LINE_X':
|
|
289
|
+
self.isX = True
|
|
290
|
+
self.log.debug("Tado X API detected")
|
|
@@ -7,10 +7,11 @@ import logging
|
|
|
7
7
|
import warnings
|
|
8
8
|
from enum import IntEnum
|
|
9
9
|
|
|
10
|
-
from .exceptions import TadoNotSupportedException
|
|
11
|
-
from PyTado.http import Action, Domain, Endpoint, Http, Mode, TadoRequest
|
|
10
|
+
from .exceptions import TadoNotSupportedException, TadoXNotSupportedException
|
|
11
|
+
from PyTado.http import Action, Domain, Endpoint, Http, Mode, TadoRequest, TadoXRequest
|
|
12
12
|
from PyTado.logging import Logger
|
|
13
13
|
from .zone import TadoZone
|
|
14
|
+
from typing import Any
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class Tado:
|
|
@@ -77,10 +78,16 @@ class Tado:
|
|
|
77
78
|
Gets device information.
|
|
78
79
|
"""
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if self.http.isX:
|
|
82
|
+
request = TadoXRequest()
|
|
83
|
+
request.command = "roomsAndDevices"
|
|
84
|
+
rooms: list[dict[str, Any]] = self.http.request(request)['rooms']
|
|
85
|
+
devices = [device for room in rooms for device in room['devices']]
|
|
86
|
+
return devices
|
|
87
|
+
else:
|
|
88
|
+
request = TadoRequest()
|
|
89
|
+
request.command = "devices"
|
|
90
|
+
return self.http.request(request)
|
|
84
91
|
|
|
85
92
|
# <editor-fold desc="Deprecated">
|
|
86
93
|
def getZones(self):
|
|
@@ -96,10 +103,15 @@ class Tado:
|
|
|
96
103
|
Gets zones information.
|
|
97
104
|
"""
|
|
98
105
|
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
if self.http.isX:
|
|
107
|
+
request = TadoXRequest()
|
|
108
|
+
request.command = "roomsAndDevices"
|
|
109
|
+
return self.http.request(request)['rooms']
|
|
110
|
+
else:
|
|
111
|
+
request = TadoRequest()
|
|
112
|
+
request.command = "zones"
|
|
113
|
+
return self.http.request(request)
|
|
101
114
|
|
|
102
|
-
return self.http.request(request)
|
|
103
115
|
|
|
104
116
|
# <editor-fold desc="Deprecated">
|
|
105
117
|
def getZoneState(self, zone):
|
|
@@ -114,8 +126,10 @@ class Tado:
|
|
|
114
126
|
"""
|
|
115
127
|
Gets current state of Zone as a TadoZone object.
|
|
116
128
|
"""
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
if self.http.isX:
|
|
130
|
+
return TadoZone(self.get_state(zone), zone, isX=True)
|
|
131
|
+
else:
|
|
132
|
+
return TadoZone(self.get_state(zone), zone)
|
|
119
133
|
|
|
120
134
|
# <editor-fold desc="Deprecated">
|
|
121
135
|
def getZoneStates(self):
|
|
@@ -131,8 +145,12 @@ class Tado:
|
|
|
131
145
|
Gets current states of all zones.
|
|
132
146
|
"""
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
if self.http.isX:
|
|
149
|
+
request = TadoXRequest()
|
|
150
|
+
request.command = "rooms"
|
|
151
|
+
else:
|
|
152
|
+
request = TadoRequest()
|
|
153
|
+
request.command = "zoneStates"
|
|
136
154
|
|
|
137
155
|
return self.http.request(request)
|
|
138
156
|
|
|
@@ -149,10 +167,14 @@ class Tado:
|
|
|
149
167
|
"""
|
|
150
168
|
Gets current state of Zone.
|
|
151
169
|
"""
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
170
|
+
if self.http.isX:
|
|
171
|
+
request = TadoXRequest()
|
|
172
|
+
request.command = f"rooms/{zone}"
|
|
173
|
+
data = self.http.request(request)
|
|
174
|
+
else:
|
|
175
|
+
request = TadoRequest()
|
|
176
|
+
request.command = f"zones/{zone}/state"
|
|
177
|
+
data = {**self.http.request(request), **self.get_zone_overlay_default(zone)}
|
|
156
178
|
|
|
157
179
|
return data
|
|
158
180
|
|
|
@@ -236,7 +258,10 @@ class Tado:
|
|
|
236
258
|
def get_capabilities(self, zone):
|
|
237
259
|
"""
|
|
238
260
|
Gets current capabilities of zone.
|
|
261
|
+
TODO: This method is not currently supported by the Tado X API, check if it is needed
|
|
239
262
|
"""
|
|
263
|
+
if self.http.isX:
|
|
264
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
240
265
|
|
|
241
266
|
request = TadoRequest()
|
|
242
267
|
request.command = f"zones/{zone:d}/capabilities"
|
|
@@ -258,7 +283,11 @@ class Tado:
|
|
|
258
283
|
"""
|
|
259
284
|
|
|
260
285
|
data = self.get_state(zone)['sensorDataPoints']
|
|
261
|
-
|
|
286
|
+
if self.http.isX:
|
|
287
|
+
return {'temperature': data['insideTemperature']['value'],
|
|
288
|
+
'humidity': data['humidity']['percentage']}
|
|
289
|
+
else:
|
|
290
|
+
return {'temperature': data['insideTemperature']['celsius'],
|
|
262
291
|
'humidity': data['humidity']['percentage']}
|
|
263
292
|
|
|
264
293
|
# <editor-fold desc="Deprecated">
|
|
@@ -275,15 +304,21 @@ class Tado:
|
|
|
275
304
|
Get the Timetable type currently active
|
|
276
305
|
"""
|
|
277
306
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
307
|
+
if self.http.isX:
|
|
308
|
+
# Not currently supported by the Tado X API
|
|
309
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
310
|
+
else:
|
|
311
|
+
request = TadoRequest()
|
|
312
|
+
request.command = f"zones/{zone:d}/schedule/activeTimetable"
|
|
313
|
+
request.mode = Mode.PLAIN
|
|
314
|
+
data = self.http.request(request)
|
|
315
|
+
|
|
316
|
+
if "id" in data:
|
|
317
|
+
return Tado.Timetable(data["id"])
|
|
318
|
+
|
|
319
|
+
raise Exception(f"Returned data did not contain \"id\" : {str(data)}")
|
|
282
320
|
|
|
283
|
-
if "id" in data:
|
|
284
|
-
return Tado.Timetable(data["id"])
|
|
285
321
|
|
|
286
|
-
raise Exception(f"Returned data did not contain \"id\" : {str(data)}")
|
|
287
322
|
|
|
288
323
|
# <editor-fold desc="Deprecated">
|
|
289
324
|
def getHistoric(self, zone, date):
|
|
@@ -328,6 +363,10 @@ class Tado:
|
|
|
328
363
|
id = 3 : SEVEN_DAY (MONDAY, TUESDAY, WEDNESDAY ...)
|
|
329
364
|
"""
|
|
330
365
|
|
|
366
|
+
if self.http.isX:
|
|
367
|
+
# Not currently supported by the Tado X API
|
|
368
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
369
|
+
|
|
331
370
|
# Type checking
|
|
332
371
|
if not isinstance(_id, Tado.Timetable):
|
|
333
372
|
raise TypeError('id must be an instance of Tado.Timetable')
|
|
@@ -350,12 +389,19 @@ class Tado:
|
|
|
350
389
|
|
|
351
390
|
# </editor-fold>
|
|
352
391
|
|
|
353
|
-
def get_schedule(self, zone, _id, day=None):
|
|
392
|
+
def get_schedule(self, zone, _id=None, day=None):
|
|
354
393
|
"""
|
|
355
394
|
Get the JSON representation of the schedule for a zone.
|
|
356
395
|
Zone has 3 different schedules, one for each timetable (see setTimetable)
|
|
357
396
|
"""
|
|
358
397
|
|
|
398
|
+
if self.http.isX:
|
|
399
|
+
request = TadoXRequest()
|
|
400
|
+
request.command = f'rooms/{zone}/schedule'
|
|
401
|
+
return self.http.request(request)
|
|
402
|
+
|
|
403
|
+
if not _id:
|
|
404
|
+
raise ValueError('id must be an instance of Tado.Timetable')
|
|
359
405
|
# Type checking
|
|
360
406
|
if not isinstance(_id, Tado.Timetable):
|
|
361
407
|
raise TypeError('id must be an instance of Tado.Timetable')
|
|
@@ -377,14 +423,25 @@ class Tado:
|
|
|
377
423
|
|
|
378
424
|
# </editor-fold>
|
|
379
425
|
|
|
380
|
-
def set_schedule(self, zone, _id, day
|
|
426
|
+
def set_schedule(self, zone, data, _id=None, day=None):
|
|
381
427
|
"""
|
|
382
|
-
Set the schedule for a zone, day is required
|
|
428
|
+
Set the schedule for a zone, day is required for non-X API
|
|
383
429
|
"""
|
|
384
430
|
|
|
431
|
+
# TODO: sometimes gives back 400 error
|
|
432
|
+
if self.http.isX:
|
|
433
|
+
request = TadoXRequest()
|
|
434
|
+
request.action = Action.SET
|
|
435
|
+
request.command = f'rooms/{zone}/schedule'
|
|
436
|
+
request.payload = data
|
|
437
|
+
request.mode = Mode.OBJECT
|
|
438
|
+
return self.http.request(request)
|
|
439
|
+
|
|
385
440
|
# Type checking
|
|
386
441
|
if not isinstance(_id, Tado.Timetable):
|
|
387
442
|
raise TypeError('id must be an instance of Tado.Timetable')
|
|
443
|
+
if not isinstance(day, int):
|
|
444
|
+
raise ValueError('day must be an integer')
|
|
388
445
|
|
|
389
446
|
request = TadoRequest()
|
|
390
447
|
request.command = f"zones/{zone:d}/schedule/timetables/{_id:d}/blocks/{day}"
|
|
@@ -427,7 +484,10 @@ class Tado:
|
|
|
427
484
|
Gets air quality information
|
|
428
485
|
"""
|
|
429
486
|
|
|
430
|
-
|
|
487
|
+
if self.http.isX:
|
|
488
|
+
request = TadoXRequest()
|
|
489
|
+
else:
|
|
490
|
+
request = TadoRequest()
|
|
431
491
|
request.command = "airComfort"
|
|
432
492
|
|
|
433
493
|
return self.http.request(request)
|
|
@@ -461,6 +521,11 @@ class Tado:
|
|
|
461
521
|
Gets getAppUsersRelativePositions data
|
|
462
522
|
"""
|
|
463
523
|
|
|
524
|
+
if self.http.isX:
|
|
525
|
+
# Not currently supported by the Tado X API
|
|
526
|
+
# TODO: reverse engineer mobile API
|
|
527
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
528
|
+
|
|
464
529
|
request = TadoRequest()
|
|
465
530
|
request.command = "getAppUsersRelativePositions"
|
|
466
531
|
request.endpoint = Endpoint.MOBILE
|
|
@@ -500,12 +565,19 @@ class Tado:
|
|
|
500
565
|
Delete current overlay
|
|
501
566
|
"""
|
|
502
567
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
568
|
+
if self.http.isX:
|
|
569
|
+
request = TadoXRequest()
|
|
570
|
+
request.command = f"rooms/{zone}/resumeSchedule"
|
|
571
|
+
request.action = Action.SET
|
|
507
572
|
|
|
508
|
-
|
|
573
|
+
return self.http.request(request)
|
|
574
|
+
else:
|
|
575
|
+
request = TadoRequest()
|
|
576
|
+
request.command = f"zones/{zone:d}/overlay"
|
|
577
|
+
request.action = Action.RESET
|
|
578
|
+
request.mode = Mode.PLAIN
|
|
579
|
+
|
|
580
|
+
return self.http.request(request)
|
|
509
581
|
|
|
510
582
|
# <editor-fold desc="Deprecated">
|
|
511
583
|
def setZoneOverlay(self, zone, overlayMode, setTemp=None, duration=None, deviceType='HEATING', power="ON",
|
|
@@ -540,9 +612,24 @@ class Tado:
|
|
|
540
612
|
|
|
541
613
|
post_data = {
|
|
542
614
|
"setting": {"type": device_type, "power": power},
|
|
543
|
-
"termination": {"
|
|
615
|
+
"termination": {"type": overlay_mode},
|
|
544
616
|
}
|
|
545
617
|
|
|
618
|
+
if self.http.isX:
|
|
619
|
+
if set_temp is not None:
|
|
620
|
+
post_data["setting"]["temperature"] = {"value": set_temp, "valueRaw": set_temp, "precision": 0.1}
|
|
621
|
+
|
|
622
|
+
if duration is not None:
|
|
623
|
+
post_data["termination"]["durationInSeconds"] = duration
|
|
624
|
+
|
|
625
|
+
request = TadoXRequest()
|
|
626
|
+
request.command = f"rooms/{zone}/manualControl"
|
|
627
|
+
request.action = Action.SET
|
|
628
|
+
request.payload = post_data
|
|
629
|
+
|
|
630
|
+
return self.http.request(request)
|
|
631
|
+
|
|
632
|
+
|
|
546
633
|
if set_temp is not None:
|
|
547
634
|
post_data["setting"]["temperature"] = {"celsius": set_temp}
|
|
548
635
|
if fan_speed is not None:
|
|
@@ -584,6 +671,10 @@ class Tado:
|
|
|
584
671
|
Get current overlay default settings for zone.
|
|
585
672
|
"""
|
|
586
673
|
|
|
674
|
+
if self.http.isX:
|
|
675
|
+
# Not currently supported by the Tado X API
|
|
676
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
677
|
+
|
|
587
678
|
request = TadoRequest()
|
|
588
679
|
request.command = f"zones/{zone:d}/defaultOverlay"
|
|
589
680
|
|
|
@@ -698,6 +789,12 @@ class Tado:
|
|
|
698
789
|
|
|
699
790
|
data = self.get_state(zone)
|
|
700
791
|
|
|
792
|
+
if self.http.isX:
|
|
793
|
+
if 'activated'in data['openWindow']:
|
|
794
|
+
return {'openWindowDetected': True}
|
|
795
|
+
else:
|
|
796
|
+
return {'openWindowDetected': False}
|
|
797
|
+
|
|
701
798
|
if "openWindowDetected" in data:
|
|
702
799
|
return {'openWindowDetected': data['openWindowDetected']}
|
|
703
800
|
else:
|
|
@@ -718,6 +815,10 @@ class Tado:
|
|
|
718
815
|
Note: This can only be set if an open window was detected in this zone
|
|
719
816
|
"""
|
|
720
817
|
|
|
818
|
+
if self.http.isX:
|
|
819
|
+
# Not currently supported by the Tado X API
|
|
820
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
821
|
+
|
|
721
822
|
request = TadoRequest()
|
|
722
823
|
request.command = f"zones/{zone:d}/state/openWindow/activate"
|
|
723
824
|
request.action = Action.SET
|
|
@@ -739,6 +840,10 @@ class Tado:
|
|
|
739
840
|
Sets the window in zone to closed
|
|
740
841
|
"""
|
|
741
842
|
|
|
843
|
+
if self.http.isX:
|
|
844
|
+
# TODO test with X
|
|
845
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
846
|
+
|
|
742
847
|
request = TadoRequest()
|
|
743
848
|
request.command = f"zones/{zone:d}/state/openWindow"
|
|
744
849
|
request.action = Action.RESET
|
|
@@ -761,6 +866,12 @@ class Tado:
|
|
|
761
866
|
Gets information about devices
|
|
762
867
|
with option to get specific info i.e. cmd='temperatureOffset'
|
|
763
868
|
"""
|
|
869
|
+
|
|
870
|
+
if self.http.isX:
|
|
871
|
+
# Not currently supported by the Tado X API
|
|
872
|
+
# Is included in the roomsAndDevices endpoint
|
|
873
|
+
raise TadoXNotSupportedException("This method is not currently supported by the Tado X API")
|
|
874
|
+
|
|
764
875
|
request = TadoRequest()
|
|
765
876
|
request.command = cmd
|
|
766
877
|
request.action = Action.GET
|
|
@@ -783,6 +894,15 @@ class Tado:
|
|
|
783
894
|
Set the Temperature offset on the device.
|
|
784
895
|
"""
|
|
785
896
|
|
|
897
|
+
if self.http.isX:
|
|
898
|
+
request = TadoXRequest()
|
|
899
|
+
request.command = f"roomsAndDevices/devices/{device_id}"
|
|
900
|
+
request.action = Action.UPDATE
|
|
901
|
+
request.payload = {"temperatureOffset": offset}
|
|
902
|
+
|
|
903
|
+
return self.http.request(request)
|
|
904
|
+
|
|
905
|
+
|
|
786
906
|
request = TadoRequest()
|
|
787
907
|
request.command = "temperatureOffset"
|
|
788
908
|
request.action = Action.CHANGE
|
|
@@ -914,6 +1034,7 @@ class Tado:
|
|
|
914
1034
|
def get_zone_control(self, zone):
|
|
915
1035
|
"""
|
|
916
1036
|
Get zone control information
|
|
1037
|
+
TODO: Test with X
|
|
917
1038
|
"""
|
|
918
1039
|
|
|
919
1040
|
request = TadoRequest()
|
|
@@ -924,6 +1045,7 @@ class Tado:
|
|
|
924
1045
|
def set_zone_heating_circuit(self, zone, heating_circuit):
|
|
925
1046
|
"""
|
|
926
1047
|
Sets the heating circuit for a zone
|
|
1048
|
+
TODO: Test with X
|
|
927
1049
|
"""
|
|
928
1050
|
|
|
929
1051
|
request = TadoRequest()
|
|
@@ -9,6 +9,7 @@ from .const import (
|
|
|
9
9
|
CONST_HVAC_IDLE,
|
|
10
10
|
CONST_HVAC_OFF,
|
|
11
11
|
CONST_LINK_OFFLINE,
|
|
12
|
+
CONST_MODE_HEAT,
|
|
12
13
|
CONST_MODE_OFF,
|
|
13
14
|
CONST_MODE_SMART_SCHEDULE,
|
|
14
15
|
DEFAULT_TADO_PRECISION,
|
|
@@ -27,7 +28,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
27
28
|
class TadoZone:
|
|
28
29
|
"""Represent a tado zone."""
|
|
29
30
|
|
|
30
|
-
def __init__(self, data, zone_id):
|
|
31
|
+
def __init__(self, data, zone_id, isX: bool = False):
|
|
31
32
|
"""Create a tado zone."""
|
|
32
33
|
self._data = data
|
|
33
34
|
self._zone_id = zone_id
|
|
@@ -54,7 +55,6 @@ class TadoZone:
|
|
|
54
55
|
self._heating_power = None
|
|
55
56
|
self._heating_power_percentage = None
|
|
56
57
|
self._tado_mode = None
|
|
57
|
-
self._overlay_active = None
|
|
58
58
|
self._overlay_termination_type = None
|
|
59
59
|
self._overlay_termination_timestamp = None
|
|
60
60
|
self._preparation = None
|
|
@@ -64,6 +64,7 @@ class TadoZone:
|
|
|
64
64
|
self._precision = DEFAULT_TADO_PRECISION
|
|
65
65
|
self._default_overlay_termination_type = None
|
|
66
66
|
self._default_overlay_termination_duration = None
|
|
67
|
+
self.isX = isX
|
|
67
68
|
self.update_data(data)
|
|
68
69
|
|
|
69
70
|
@property
|
|
@@ -238,11 +239,13 @@ class TadoZone:
|
|
|
238
239
|
sensor_data = data["sensorDataPoints"]
|
|
239
240
|
|
|
240
241
|
if "insideTemperature" in sensor_data:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
if self.isX:
|
|
243
|
+
self._current_temp = float(sensor_data["insideTemperature"]["value"])
|
|
244
|
+
self._current_temp_timestamp = None
|
|
245
|
+
else:
|
|
246
|
+
temperature = float(sensor_data["insideTemperature"]["celsius"])
|
|
247
|
+
self._current_temp = temperature
|
|
248
|
+
self._current_temp_timestamp = sensor_data["insideTemperature"]["timestamp"]
|
|
246
249
|
if "precision" in sensor_data["insideTemperature"]:
|
|
247
250
|
self._precision = sensor_data["insideTemperature"]["precision"][
|
|
248
251
|
"celsius"
|
|
@@ -251,7 +254,10 @@ class TadoZone:
|
|
|
251
254
|
if "humidity" in sensor_data:
|
|
252
255
|
humidity = float(sensor_data["humidity"]["percentage"])
|
|
253
256
|
self._current_humidity = humidity
|
|
254
|
-
self.
|
|
257
|
+
if self.isX:
|
|
258
|
+
self._current_humidity_timestamp = None
|
|
259
|
+
else:
|
|
260
|
+
self._current_humidity_timestamp = sensor_data["humidity"]["timestamp"]
|
|
255
261
|
|
|
256
262
|
self._is_away = None
|
|
257
263
|
self._tado_mode = None
|
|
@@ -259,9 +265,12 @@ class TadoZone:
|
|
|
259
265
|
self._is_away = data["tadoMode"] == "AWAY"
|
|
260
266
|
self._tado_mode = data["tadoMode"]
|
|
261
267
|
|
|
268
|
+
|
|
262
269
|
self._link = None
|
|
263
270
|
if "link" in data:
|
|
264
271
|
self._link = data["link"]["state"]
|
|
272
|
+
if "connection" in data:
|
|
273
|
+
self._link = data["connection"]["state"]
|
|
265
274
|
|
|
266
275
|
self._current_hvac_action = CONST_HVAC_OFF
|
|
267
276
|
|
|
@@ -271,8 +280,11 @@ class TadoZone:
|
|
|
271
280
|
"temperature" in data["setting"]
|
|
272
281
|
and data["setting"]["temperature"] is not None
|
|
273
282
|
):
|
|
274
|
-
|
|
275
|
-
|
|
283
|
+
if self.isX:
|
|
284
|
+
self._target_temp = float(data["setting"]["temperature"]["value"])
|
|
285
|
+
else:
|
|
286
|
+
setting = float(data["setting"]["temperature"]["celsius"])
|
|
287
|
+
self._target_temp = setting
|
|
276
288
|
|
|
277
289
|
setting = data["setting"]
|
|
278
290
|
|
|
@@ -378,5 +390,21 @@ class TadoZone:
|
|
|
378
390
|
if "terminationCondition" in data:
|
|
379
391
|
self._default_overlay_termination_type = data["terminationCondition"].get('type',None)
|
|
380
392
|
self._default_overlay_termination_duration = data["terminationCondition"].get('durationInSeconds',None)
|
|
393
|
+
|
|
394
|
+
if self.isX:
|
|
395
|
+
if self._power == "ON":
|
|
396
|
+
self._current_hvac_action = CONST_HVAC_IDLE if data["heatingPower"]["percentage"] == 0 else CONST_HVAC_HEAT
|
|
397
|
+
self._heating_power_percentage = data["heatingPower"]["percentage"]
|
|
398
|
+
else:
|
|
399
|
+
self._heating_power_percentage = 0
|
|
400
|
+
self._current_hvac_action = CONST_HVAC_OFF
|
|
381
401
|
|
|
382
|
-
|
|
402
|
+
if "manualControlTermination" in data:
|
|
403
|
+
if data["manualControlTermination"]:
|
|
404
|
+
self._current_hvac_mode = CONST_MODE_HEAT if self._power == "ON" else CONST_MODE_OFF
|
|
405
|
+
self._overlay_termination_type = data["manualControlTermination"]["type"]
|
|
406
|
+
self._overlay_termination_timestamp = data["manualControlTermination"]["projectedExpiry"]
|
|
407
|
+
else:
|
|
408
|
+
self._overlay_termination_type = None
|
|
409
|
+
self._overlay_termination_timestamp = None
|
|
410
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-tado
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.7
|
|
4
4
|
Summary: PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon
|
|
5
5
|
Home-page: https://github.com/wmalgadey/PyTado
|
|
6
6
|
Author: chrism0dwk, w.malgadey
|
|
@@ -19,7 +19,7 @@ readme = open(here('README.md')).read()
|
|
|
19
19
|
requirements = [x.strip() for x in open(here('requirements.txt')).readlines()]
|
|
20
20
|
|
|
21
21
|
setup(name='python-tado',
|
|
22
|
-
version='0.17.
|
|
22
|
+
version='0.17.7',
|
|
23
23
|
description='PyTado from chrism0dwk, modfied by w.malgadey, diplix, michaelarnauts, LenhartStephan, splifter, syssi, andersonshatch, Yippy, p0thi, Coffee2CodeNL, chiefdragon',
|
|
24
24
|
long_description=readme,
|
|
25
25
|
keywords='tado',
|
|
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
|