aioairzone-cloud 0.0.3__py3-none-any.whl
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.
- aioairzone_cloud/__init__.py +1 -0
- aioairzone_cloud/cloudapi.py +372 -0
- aioairzone_cloud/common.py +69 -0
- aioairzone_cloud/const.py +125 -0
- aioairzone_cloud/device.py +106 -0
- aioairzone_cloud/exceptions.py +26 -0
- aioairzone_cloud/installation.py +48 -0
- aioairzone_cloud/system.py +16 -0
- aioairzone_cloud/webserver.py +120 -0
- aioairzone_cloud/zone.py +472 -0
- aioairzone_cloud-0.0.3.dist-info/LICENSE +201 -0
- aioairzone_cloud-0.0.3.dist-info/METADATA +41 -0
- aioairzone_cloud-0.0.3.dist-info/RECORD +15 -0
- aioairzone_cloud-0.0.3.dist-info/WHEEL +5 -0
- aioairzone_cloud-0.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Airzone Cloud API library."""
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"""Airzone Cloud API."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import urllib.parse
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
from aiohttp import ClientConnectorError, ClientSession
|
|
9
|
+
from aiohttp.client_reqrep import ClientResponse
|
|
10
|
+
|
|
11
|
+
from .common import ConnectionOptions
|
|
12
|
+
from .const import (
|
|
13
|
+
API_AUTH_LOGIN,
|
|
14
|
+
API_AUTH_REFRESH_TOKEN,
|
|
15
|
+
API_AZ_SYSTEM,
|
|
16
|
+
API_AZ_ZONE,
|
|
17
|
+
API_CONFIG,
|
|
18
|
+
API_DEVICE_ID,
|
|
19
|
+
API_DEVICE_TYPE,
|
|
20
|
+
API_DEVICES,
|
|
21
|
+
API_EMAIL,
|
|
22
|
+
API_INSTALLATION_ID,
|
|
23
|
+
API_INSTALLATIONS,
|
|
24
|
+
API_PASSWORD,
|
|
25
|
+
API_REFRESH_TOKEN,
|
|
26
|
+
API_STATUS,
|
|
27
|
+
API_TOKEN,
|
|
28
|
+
API_URL,
|
|
29
|
+
API_USER,
|
|
30
|
+
API_USER_LOGOUT,
|
|
31
|
+
API_V1,
|
|
32
|
+
API_WS,
|
|
33
|
+
AZD_INSTALLATIONS,
|
|
34
|
+
AZD_SYSTEMS,
|
|
35
|
+
AZD_WEBSERVERS,
|
|
36
|
+
AZD_ZONES,
|
|
37
|
+
HEADER_AUTHORIZATION,
|
|
38
|
+
HEADER_BEARER,
|
|
39
|
+
HTTP_CALL_TIMEOUT,
|
|
40
|
+
)
|
|
41
|
+
from .device import Device
|
|
42
|
+
from .exceptions import LoginError, TokenRefreshError
|
|
43
|
+
from .installation import Installation
|
|
44
|
+
from .system import System
|
|
45
|
+
from .webserver import WebServer
|
|
46
|
+
from .zone import Zone
|
|
47
|
+
|
|
48
|
+
_LOGGER = logging.getLogger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AirzoneCloudApi:
|
|
52
|
+
"""Airzone Cloud API."""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
aiohttp_session: ClientSession,
|
|
57
|
+
options: ConnectionOptions,
|
|
58
|
+
):
|
|
59
|
+
"""Airzone Cloud API init."""
|
|
60
|
+
self.aiohttp_session = aiohttp_session
|
|
61
|
+
self.installations: list[Installation] = []
|
|
62
|
+
self.options = options
|
|
63
|
+
self.refresh_token: str | None = None
|
|
64
|
+
self.systems: list[System] = []
|
|
65
|
+
self.token: str | None = None
|
|
66
|
+
self.webservers: list[WebServer] = []
|
|
67
|
+
self.zones: list[Zone] = []
|
|
68
|
+
|
|
69
|
+
async def api_request(
|
|
70
|
+
self, method: str, path: str, json: Any | None = None
|
|
71
|
+
) -> dict[str, Any]:
|
|
72
|
+
"""Airzone Cloud API request."""
|
|
73
|
+
_LOGGER.debug("aiohttp request: /%s (params=%s)", path, json)
|
|
74
|
+
headers: dict[str, str] = {}
|
|
75
|
+
if self.token:
|
|
76
|
+
headers[HEADER_AUTHORIZATION] = f"{HEADER_BEARER} {self.token}"
|
|
77
|
+
resp: ClientResponse = await self.aiohttp_session.request(
|
|
78
|
+
method,
|
|
79
|
+
f"{API_URL}/{path}",
|
|
80
|
+
headers=headers,
|
|
81
|
+
json=json,
|
|
82
|
+
raise_for_status=True,
|
|
83
|
+
timeout=HTTP_CALL_TIMEOUT,
|
|
84
|
+
)
|
|
85
|
+
resp_json = await resp.json(content_type=None)
|
|
86
|
+
_LOGGER.debug("aiohttp response: %s", resp_json)
|
|
87
|
+
return cast(dict, resp_json)
|
|
88
|
+
|
|
89
|
+
async def api_get_device_config(self, device: Device) -> dict[str, Any]:
|
|
90
|
+
"""Request API device config data."""
|
|
91
|
+
dev_id = urllib.parse.quote(device.get_id())
|
|
92
|
+
|
|
93
|
+
params = {
|
|
94
|
+
API_INSTALLATION_ID: device.get_installation(),
|
|
95
|
+
}
|
|
96
|
+
dev_params = urllib.parse.urlencode(params)
|
|
97
|
+
|
|
98
|
+
return await self.api_request(
|
|
99
|
+
"GET",
|
|
100
|
+
f"{API_V1}/{API_DEVICES}/{dev_id}/{API_CONFIG}?{dev_params}",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def api_get_device_status(self, device: Device) -> dict[str, Any]:
|
|
104
|
+
"""Request API device status data."""
|
|
105
|
+
dev_id = urllib.parse.quote(device.get_id())
|
|
106
|
+
|
|
107
|
+
params = {
|
|
108
|
+
API_INSTALLATION_ID: device.get_installation(),
|
|
109
|
+
}
|
|
110
|
+
dev_params = urllib.parse.urlencode(params)
|
|
111
|
+
|
|
112
|
+
return await self.api_request(
|
|
113
|
+
"GET",
|
|
114
|
+
f"{API_V1}/{API_DEVICES}/{dev_id}/{API_STATUS}?{dev_params}",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
async def api_get_installation(self, installation: Installation) -> dict[str, Any]:
|
|
118
|
+
"""Request API installation data."""
|
|
119
|
+
inst_id = urllib.parse.quote(installation.get_id())
|
|
120
|
+
return await self.api_request(
|
|
121
|
+
"GET",
|
|
122
|
+
f"{API_V1}/{API_INSTALLATIONS}/{inst_id}",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
async def api_get_installations(self) -> dict[str, Any]:
|
|
126
|
+
"""Request API installations data."""
|
|
127
|
+
return await self.api_request(
|
|
128
|
+
"GET",
|
|
129
|
+
f"{API_V1}/{API_INSTALLATIONS}",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
async def api_get_user(self) -> dict[str, Any]:
|
|
133
|
+
"""Request API user data."""
|
|
134
|
+
return await self.api_request(
|
|
135
|
+
"GET",
|
|
136
|
+
f"{API_V1}/{API_USER}",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def api_get_webserver(
|
|
140
|
+
self, webserver: WebServer, devices: bool
|
|
141
|
+
) -> dict[str, Any]:
|
|
142
|
+
"""Request API webserver data."""
|
|
143
|
+
ws_id = urllib.parse.quote(webserver.get_id())
|
|
144
|
+
|
|
145
|
+
params: dict[str, Any] = {
|
|
146
|
+
API_INSTALLATION_ID: webserver.get_installation(),
|
|
147
|
+
}
|
|
148
|
+
if devices:
|
|
149
|
+
params[API_DEVICES] = 1
|
|
150
|
+
ws_params = urllib.parse.urlencode(params)
|
|
151
|
+
|
|
152
|
+
return await self.api_request(
|
|
153
|
+
"GET",
|
|
154
|
+
f"{API_V1}/{API_DEVICES}/{API_WS}/{ws_id}/{API_STATUS}?{ws_params}",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
async def login(self) -> None:
|
|
158
|
+
"""Perform Airzone Cloud API login."""
|
|
159
|
+
if self.token:
|
|
160
|
+
await self.logout()
|
|
161
|
+
resp = await self.api_request(
|
|
162
|
+
"POST",
|
|
163
|
+
f"{API_V1}/{API_AUTH_LOGIN}",
|
|
164
|
+
{
|
|
165
|
+
API_EMAIL: self.options.username,
|
|
166
|
+
API_PASSWORD: self.options.password,
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
_LOGGER.debug("login resp: %s", resp)
|
|
170
|
+
if resp.keys() < {API_TOKEN, API_REFRESH_TOKEN}:
|
|
171
|
+
raise LoginError
|
|
172
|
+
self.token = resp[API_TOKEN]
|
|
173
|
+
self.refresh_token = resp[API_REFRESH_TOKEN]
|
|
174
|
+
|
|
175
|
+
async def logout(self) -> None:
|
|
176
|
+
"""Perform Airzone Cloud API logout."""
|
|
177
|
+
try:
|
|
178
|
+
if self.token:
|
|
179
|
+
await self.api_request(
|
|
180
|
+
"GET",
|
|
181
|
+
f"{API_V1}/{API_USER_LOGOUT}",
|
|
182
|
+
)
|
|
183
|
+
except ClientConnectorError:
|
|
184
|
+
pass
|
|
185
|
+
finally:
|
|
186
|
+
self.refresh_token = None
|
|
187
|
+
self.token = None
|
|
188
|
+
|
|
189
|
+
async def token_refresh(self) -> None:
|
|
190
|
+
"""Perform Airzone Cloud API token refresh."""
|
|
191
|
+
if self.token and self.refresh_token:
|
|
192
|
+
resp = await self.api_request(
|
|
193
|
+
"POST",
|
|
194
|
+
f"{API_V1}/{API_AUTH_REFRESH_TOKEN}",
|
|
195
|
+
{
|
|
196
|
+
API_REFRESH_TOKEN: self.refresh_token,
|
|
197
|
+
},
|
|
198
|
+
)
|
|
199
|
+
_LOGGER.debug("refresh resp: %s", resp)
|
|
200
|
+
if resp.keys() < {API_TOKEN, API_REFRESH_TOKEN}:
|
|
201
|
+
raise TokenRefreshError
|
|
202
|
+
self.token = resp[API_TOKEN]
|
|
203
|
+
self.refresh_token = resp[API_REFRESH_TOKEN]
|
|
204
|
+
|
|
205
|
+
def data(self) -> dict[str, Any]:
|
|
206
|
+
"""Return Airzone Cloud data."""
|
|
207
|
+
data: dict[str, Any] = {}
|
|
208
|
+
|
|
209
|
+
if self.installations:
|
|
210
|
+
installations: dict[str, Any] = {}
|
|
211
|
+
for installation in self.installations:
|
|
212
|
+
installations[installation.get_id()] = installation.data()
|
|
213
|
+
data[AZD_INSTALLATIONS] = installations
|
|
214
|
+
|
|
215
|
+
if self.systems:
|
|
216
|
+
systems: dict[str, Any] = {}
|
|
217
|
+
for system in self.systems:
|
|
218
|
+
systems[system.get_id()] = system.data()
|
|
219
|
+
data[AZD_SYSTEMS] = systems
|
|
220
|
+
|
|
221
|
+
if self.webservers:
|
|
222
|
+
webservers: dict[str, Any] = {}
|
|
223
|
+
for webserver in self.webservers:
|
|
224
|
+
webservers[webserver.get_id()] = webserver.data()
|
|
225
|
+
data[AZD_WEBSERVERS] = webservers
|
|
226
|
+
|
|
227
|
+
if self.zones:
|
|
228
|
+
zones: dict[str, Any] = {}
|
|
229
|
+
for zone in self.zones:
|
|
230
|
+
zones[zone.get_id()] = zone.data()
|
|
231
|
+
data[AZD_ZONES] = zones
|
|
232
|
+
|
|
233
|
+
return data
|
|
234
|
+
|
|
235
|
+
def get_installation_id(self, inst_id: str) -> Installation | None:
|
|
236
|
+
"""Return Airzone Cloud Installation by ID."""
|
|
237
|
+
for inst in self.installations:
|
|
238
|
+
if inst_id == inst.get_id():
|
|
239
|
+
return inst
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
def get_system_id(self, sys_id: str) -> System | None:
|
|
243
|
+
"""Return Airzone Cloud System by ID."""
|
|
244
|
+
for system in self.systems:
|
|
245
|
+
if sys_id == system.get_id():
|
|
246
|
+
return system
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
def get_webserver_id(self, ws_id: str) -> WebServer | None:
|
|
250
|
+
"""Return Airzone Cloud WebServer by ID."""
|
|
251
|
+
for ws in self.webservers:
|
|
252
|
+
if ws_id == ws.get_id():
|
|
253
|
+
return ws
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
def get_zone_id(self, zone_id: str) -> Zone | None:
|
|
257
|
+
"""Return Airzone Cloud Zone by ID."""
|
|
258
|
+
for zone in self.zones:
|
|
259
|
+
if zone_id == zone.get_id():
|
|
260
|
+
return zone
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
async def list_installations(self) -> list[Installation]:
|
|
264
|
+
"""Return Airzone Cloud installations list."""
|
|
265
|
+
inst_list: list[Installation] = []
|
|
266
|
+
|
|
267
|
+
inst_data = await self.api_get_installations()
|
|
268
|
+
for inst in inst_data[API_INSTALLATIONS]:
|
|
269
|
+
inst_list.append(Installation(inst))
|
|
270
|
+
|
|
271
|
+
return inst_list
|
|
272
|
+
|
|
273
|
+
def select_installation(self, inst: Installation) -> None:
|
|
274
|
+
"""Select single Airzone Cloud installation."""
|
|
275
|
+
self.installations = [inst]
|
|
276
|
+
for ws_id in inst.get_webservers():
|
|
277
|
+
if not self.get_webserver_id(ws_id):
|
|
278
|
+
ws = WebServer(inst.get_id(), ws_id)
|
|
279
|
+
self.webservers.append(ws)
|
|
280
|
+
|
|
281
|
+
def set_system_zones_data(self, system: System):
|
|
282
|
+
"""Set slave zones modes from master zone."""
|
|
283
|
+
modes = system.get_modes()
|
|
284
|
+
installation_id = system.get_installation()
|
|
285
|
+
system_id = system.get_id()
|
|
286
|
+
system_num = system.get_system()
|
|
287
|
+
for zone in self.zones:
|
|
288
|
+
zone.set_system_id(system_id)
|
|
289
|
+
is_slave = zone.get_master() is False
|
|
290
|
+
if (
|
|
291
|
+
is_slave
|
|
292
|
+
and modes
|
|
293
|
+
and zone.get_installation() == installation_id
|
|
294
|
+
and zone.get_system() == system_num
|
|
295
|
+
):
|
|
296
|
+
zone.set_modes(modes)
|
|
297
|
+
|
|
298
|
+
async def update_installations(self) -> None:
|
|
299
|
+
"""Update Airzone Cloud installations from API."""
|
|
300
|
+
installations_data = await self.api_get_installations()
|
|
301
|
+
for installation_data in installations_data[API_INSTALLATIONS]:
|
|
302
|
+
if not self.get_installation_id(installation_data[API_INSTALLATION_ID]):
|
|
303
|
+
installation = Installation(installation_data)
|
|
304
|
+
if installation:
|
|
305
|
+
self.installations.append(installation)
|
|
306
|
+
for ws_id in installation.get_webservers():
|
|
307
|
+
if not self.get_webserver_id(ws_id):
|
|
308
|
+
ws = WebServer(installation.get_id(), ws_id)
|
|
309
|
+
self.webservers.append(ws)
|
|
310
|
+
|
|
311
|
+
async def update_system(self, system: System) -> None:
|
|
312
|
+
"""Update Airzone Cloud System from API."""
|
|
313
|
+
device_data = await self.api_get_device_status(system)
|
|
314
|
+
system.update(device_data)
|
|
315
|
+
|
|
316
|
+
async def update_system_id(self, sys_id: str) -> None:
|
|
317
|
+
"""Update Airzone Cloud System by ID."""
|
|
318
|
+
system = self.get_system_id(sys_id)
|
|
319
|
+
if system:
|
|
320
|
+
await self.update_system(system)
|
|
321
|
+
|
|
322
|
+
async def update_systems(self) -> None:
|
|
323
|
+
"""Update all Airzone Cloud Systems."""
|
|
324
|
+
for system in self.systems:
|
|
325
|
+
await self.update_system(system)
|
|
326
|
+
|
|
327
|
+
async def update_webserver(self, ws: WebServer, devices: bool) -> None:
|
|
328
|
+
"""Update Airzone Cloud WebServer from API."""
|
|
329
|
+
ws_data = await self.api_get_webserver(ws, devices)
|
|
330
|
+
ws.update(ws_data)
|
|
331
|
+
if devices:
|
|
332
|
+
for device_data in ws_data[API_DEVICES]:
|
|
333
|
+
if API_AZ_ZONE == device_data[API_DEVICE_TYPE]:
|
|
334
|
+
if not self.get_zone_id(device_data[API_DEVICE_ID]):
|
|
335
|
+
zone = Zone(ws.get_installation(), ws.get_id(), device_data)
|
|
336
|
+
if zone:
|
|
337
|
+
self.zones.append(zone)
|
|
338
|
+
elif API_AZ_SYSTEM == device_data[API_DEVICE_TYPE]:
|
|
339
|
+
if not self.get_zone_id(device_data[API_DEVICE_ID]):
|
|
340
|
+
system = System(ws.get_installation(), ws.get_id(), device_data)
|
|
341
|
+
if system:
|
|
342
|
+
self.systems.append(system)
|
|
343
|
+
|
|
344
|
+
async def update_webserver_id(self, ws_id: str, devices: bool = False) -> None:
|
|
345
|
+
"""Update Airzone Cloud WebServer by ID."""
|
|
346
|
+
ws = self.get_webserver_id(ws_id)
|
|
347
|
+
if ws:
|
|
348
|
+
await self.update_webserver(ws, devices)
|
|
349
|
+
|
|
350
|
+
async def update_webservers(self, devices: bool = True) -> None:
|
|
351
|
+
"""Update all Airzone Cloud WebServers."""
|
|
352
|
+
for ws in self.webservers:
|
|
353
|
+
await self.update_webserver(ws, devices)
|
|
354
|
+
|
|
355
|
+
async def update_zone(self, zone: Zone) -> None:
|
|
356
|
+
"""Update Airzone Cloud Zone from API."""
|
|
357
|
+
device_data = await self.api_get_device_status(zone)
|
|
358
|
+
zone.update(device_data)
|
|
359
|
+
|
|
360
|
+
async def update_zone_id(self, zone_id: str) -> None:
|
|
361
|
+
"""Update Airzone Cloud Zone by ID."""
|
|
362
|
+
zone = self.get_zone_id(zone_id)
|
|
363
|
+
if zone:
|
|
364
|
+
await self.update_zone(zone)
|
|
365
|
+
|
|
366
|
+
async def update_zones(self) -> None:
|
|
367
|
+
"""Update all Airzone Cloud Zones."""
|
|
368
|
+
for zone in self.zones:
|
|
369
|
+
await self.update_zone(zone)
|
|
370
|
+
|
|
371
|
+
for system in self.systems:
|
|
372
|
+
self.set_system_zones_data(system)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Airzone Cloud API common code."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class ConnectionOptions:
|
|
10
|
+
"""Airzone Cloud options for connection."""
|
|
11
|
+
|
|
12
|
+
username: str
|
|
13
|
+
password: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OperationMode(int, Enum):
|
|
17
|
+
"""Airzone Cloud operation modes."""
|
|
18
|
+
|
|
19
|
+
STOP = 0
|
|
20
|
+
AUTO = 1
|
|
21
|
+
COOLING = 2
|
|
22
|
+
HEATING = 3
|
|
23
|
+
VENTILATION = 4
|
|
24
|
+
DRY = 5
|
|
25
|
+
EMERGENCY_HEAT = 6
|
|
26
|
+
HEAT_AIR = 7
|
|
27
|
+
HEAT_RADIANT = 8
|
|
28
|
+
HEAT_COMBINED = 9
|
|
29
|
+
COOLING_AIR = 10
|
|
30
|
+
COOLING_RADIANT = 11
|
|
31
|
+
COOLING_COMBINED = 12
|
|
32
|
+
|
|
33
|
+
def is_cool(self) -> bool:
|
|
34
|
+
"""Return if mode is Cool."""
|
|
35
|
+
return self.value in [
|
|
36
|
+
self.COOLING,
|
|
37
|
+
self.COOLING_AIR,
|
|
38
|
+
self.COOLING_COMBINED,
|
|
39
|
+
self.COOLING_RADIANT,
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def is_dry(self) -> bool:
|
|
43
|
+
"""Return if mode is Dry."""
|
|
44
|
+
return self.value in [self.DRY]
|
|
45
|
+
|
|
46
|
+
def is_heat(self) -> bool:
|
|
47
|
+
"""Return if mode is Heat."""
|
|
48
|
+
return self.value in [
|
|
49
|
+
self.EMERGENCY_HEAT,
|
|
50
|
+
self.HEATING,
|
|
51
|
+
self.HEAT_AIR,
|
|
52
|
+
self.HEAT_COMBINED,
|
|
53
|
+
self.HEAT_RADIANT,
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def is_stop(self) -> bool:
|
|
57
|
+
"""Return if mode is Stop."""
|
|
58
|
+
return self.value in [self.STOP]
|
|
59
|
+
|
|
60
|
+
def is_vent(self) -> bool:
|
|
61
|
+
"""Return if mode is Ventilation."""
|
|
62
|
+
return self.value in [self.VENTILATION]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TemperatureUnit(int, Enum):
|
|
66
|
+
"""Airzone Cloud temperature units."""
|
|
67
|
+
|
|
68
|
+
CELSIUS = 0
|
|
69
|
+
FAHRENHEIT = 1
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Airzone Cloud API constants."""
|
|
2
|
+
|
|
3
|
+
API_AUTH_LOGIN = "auth/login"
|
|
4
|
+
API_AUTH_REFRESH_TOKEN = "auth/refreshToken"
|
|
5
|
+
API_CELSIUS = "celsius"
|
|
6
|
+
API_CONFIG = "config"
|
|
7
|
+
API_CONNECTION_DATE = "connection_date"
|
|
8
|
+
API_DEVICE_ID = "device_id"
|
|
9
|
+
API_DEVICE_TYPE = "device_type"
|
|
10
|
+
API_DEVICES = "devices"
|
|
11
|
+
API_DISCONNECTION_DATE = "disconnection_date"
|
|
12
|
+
API_ECO_CONF = "eco_conf"
|
|
13
|
+
API_EMAIL = "email"
|
|
14
|
+
API_FAH = "fah"
|
|
15
|
+
API_HUMIDITY = "humidity"
|
|
16
|
+
API_INSTALLATION_ID = "installation_id"
|
|
17
|
+
API_INSTALLATIONS = "installations"
|
|
18
|
+
API_IS_CONNECTED = "isConnected"
|
|
19
|
+
API_LOCAL_TEMP = "local_temp"
|
|
20
|
+
API_MODE = "mode"
|
|
21
|
+
API_MODE_AVAIL = "mode_available"
|
|
22
|
+
API_NAME = "name"
|
|
23
|
+
API_OPTS = "opts"
|
|
24
|
+
API_PARAMS = "params"
|
|
25
|
+
API_PASSWORD = "password"
|
|
26
|
+
API_POWER = "power"
|
|
27
|
+
API_RANGE_SP_MAX_COOL_AIR = "range_sp_cool_air_max"
|
|
28
|
+
API_RANGE_SP_MAX_DRY_AIR = "range_sp_dry_air_max"
|
|
29
|
+
API_RANGE_SP_MAX_EMERHEAT_AIR = "range_sp_emerheat_air_max"
|
|
30
|
+
API_RANGE_SP_MAX_HOT_AIR = "range_sp_hot_air_max"
|
|
31
|
+
API_RANGE_SP_MAX_STOP_AIR = "range_sp_stop_air_max"
|
|
32
|
+
API_RANGE_SP_MAX_VENT_AIR = "range_sp_vent_air_max"
|
|
33
|
+
API_RANGE_SP_MIN_COOL_AIR = "range_sp_cool_air_min"
|
|
34
|
+
API_RANGE_SP_MIN_DRY_AIR = "range_sp_dry_air_min"
|
|
35
|
+
API_RANGE_SP_MIN_EMERHEAT_AIR = "range_sp_emerheat_air_min"
|
|
36
|
+
API_RANGE_SP_MIN_HOT_AIR = "range_sp_hot_air_min"
|
|
37
|
+
API_RANGE_SP_MIN_STOP_AIR = "range_sp_stop_air_min"
|
|
38
|
+
API_RANGE_SP_MIN_VENT_AIR = "range_sp_vent_air_min"
|
|
39
|
+
API_REFRESH_TOKEN = "refreshToken"
|
|
40
|
+
API_SETPOINT = "setpoint"
|
|
41
|
+
API_SLEEP = "sleep"
|
|
42
|
+
API_SP_AIR_COOL = "setpoint_air_cool"
|
|
43
|
+
API_SP_AIR_DRY = "setpoint_air_dry"
|
|
44
|
+
API_SP_AIR_HEAT = "setpoint_air_heat"
|
|
45
|
+
API_SP_AIR_STOP = "setpoint_air_stop"
|
|
46
|
+
API_SP_AIR_VENT = "setpoint_air_vent"
|
|
47
|
+
API_SPEED = "speed"
|
|
48
|
+
API_STAT_AP_MAC = "stat_ap_mac"
|
|
49
|
+
API_STAT_CHANNEL = "stat_channel"
|
|
50
|
+
API_STAT_QUALITY = "stat_quality"
|
|
51
|
+
API_STAT_SSID = "stat_ssid"
|
|
52
|
+
API_STATUS = "status"
|
|
53
|
+
API_STEP = "step"
|
|
54
|
+
API_SYSTEM_NUMBER = "system_number"
|
|
55
|
+
API_TIMER = "timer"
|
|
56
|
+
API_TOKEN = "token"
|
|
57
|
+
API_UNITS = "units"
|
|
58
|
+
API_URL = "https://m.airzonecloud.com"
|
|
59
|
+
API_USER = "user"
|
|
60
|
+
API_USER_LOGOUT = "user/logout"
|
|
61
|
+
API_USER_MODE_CONF = "usermode_conf"
|
|
62
|
+
API_V1 = "api/v1"
|
|
63
|
+
API_WS = "ws"
|
|
64
|
+
API_WS_FW = "ws_fw"
|
|
65
|
+
API_WS_IDS = "ws_ids"
|
|
66
|
+
API_WS_TYPE = "ws_type"
|
|
67
|
+
API_ZONE_NUMBER = "zone_number"
|
|
68
|
+
|
|
69
|
+
API_AZ_SYSTEM = "az_system"
|
|
70
|
+
API_AZ_ZONE = "az_zone"
|
|
71
|
+
|
|
72
|
+
API_DEFAULT_TEMP_STEP = 0.5
|
|
73
|
+
|
|
74
|
+
AZD_CONNECTED = "connected"
|
|
75
|
+
AZD_CONNECTION_DATE = "connection-date"
|
|
76
|
+
AZD_DISCONNECTION_DATE = "disconnection-date"
|
|
77
|
+
AZD_FIRMWARE = "firmware"
|
|
78
|
+
AZD_HUMIDITY = "humidity"
|
|
79
|
+
AZD_ID = "id"
|
|
80
|
+
AZD_INSTALLATION = "installation"
|
|
81
|
+
AZD_INSTALLATIONS = "installations"
|
|
82
|
+
AZD_MASTER = "master"
|
|
83
|
+
AZD_MODE = "mode"
|
|
84
|
+
AZD_MODES = "modes"
|
|
85
|
+
AZD_NAME = "name"
|
|
86
|
+
AZD_POWER = "power"
|
|
87
|
+
AZD_SYSTEM = "system"
|
|
88
|
+
AZD_SYSTEM_ID = "system-id"
|
|
89
|
+
AZD_SYSTEMS = "systems"
|
|
90
|
+
AZD_TEMP = "temperature"
|
|
91
|
+
AZD_TEMP_STEP = "temperature-step"
|
|
92
|
+
AZD_TEMP_SET = "temperature-setpoint"
|
|
93
|
+
AZD_TEMP_SET_COOL_AIR = "temperature-setpoint-cool-air"
|
|
94
|
+
AZD_TEMP_SET_DRY_AIR = "temperature-setpoint-dry-air"
|
|
95
|
+
AZD_TEMP_SET_HOT_AIR = "temperature-setpoint-hot-air"
|
|
96
|
+
AZD_TEMP_SET_STOP_AIR = "temperature-setpoint-stop-air"
|
|
97
|
+
AZD_TEMP_SET_VENT_AIR = "temperature-setpoint-vent-air"
|
|
98
|
+
AZD_TEMP_SET_MAX = "temperature-setpoint-max"
|
|
99
|
+
AZD_TEMP_SET_MAX_COOL_AIR = "temperature-setpoint-max-cool-air"
|
|
100
|
+
AZD_TEMP_SET_MAX_DRY_AIR = "temperature-setpoint-max-dry-air"
|
|
101
|
+
AZD_TEMP_SET_MAX_EMERHEAT_AIR = "temperature-setpoint-max-emerheat-air"
|
|
102
|
+
AZD_TEMP_SET_MAX_HOT_AIR = "temperature-setpoint-max-hot-air"
|
|
103
|
+
AZD_TEMP_SET_MAX_STOP_AIR = "temperature-setpoint-max-stop-air"
|
|
104
|
+
AZD_TEMP_SET_MAX_VENT_AIR = "temperature-setpoint-max-vent-air"
|
|
105
|
+
AZD_TEMP_SET_MIN_COOL_AIR = "temperature-setpoint-min-cool-air"
|
|
106
|
+
AZD_TEMP_SET_MIN_DRY_AIR = "temperature-setpoint-min-dry-air"
|
|
107
|
+
AZD_TEMP_SET_MIN_EMERHEAT_AIR = "temperature-setpoint-min-emerheat-air"
|
|
108
|
+
AZD_TEMP_SET_MIN_HOT_AIR = "temperature-setpoint-min-hot-air"
|
|
109
|
+
AZD_TEMP_SET_MIN_STOP_AIR = "temperature-setpoint-min-stop-air"
|
|
110
|
+
AZD_TEMP_SET_MIN_VENT_AIR = "temperature-setpoint-min-vent-air"
|
|
111
|
+
AZD_TEMP_SET_MIN = "temperature-setpoint-min"
|
|
112
|
+
AZD_TYPE = "type"
|
|
113
|
+
AZD_WEBSERVER = "web-server"
|
|
114
|
+
AZD_WEBSERVERS = "web-servers"
|
|
115
|
+
AZD_WIFI_CHANNEL = "wifi-channel"
|
|
116
|
+
AZD_WIFI_MAC = "wifi-mac"
|
|
117
|
+
AZD_WIFI_QUALITY = "wifi-quality"
|
|
118
|
+
AZD_WIFI_SSID = "wifi-ssid"
|
|
119
|
+
AZD_ZONE = "zone"
|
|
120
|
+
AZD_ZONES = "zones"
|
|
121
|
+
|
|
122
|
+
HEADER_AUTHORIZATION = "Authorization"
|
|
123
|
+
HEADER_BEARER = "Bearer"
|
|
124
|
+
|
|
125
|
+
HTTP_CALL_TIMEOUT = 10
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Airzone Cloud API Device."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .common import OperationMode
|
|
7
|
+
from .const import (
|
|
8
|
+
API_CONFIG,
|
|
9
|
+
API_DEVICE_ID,
|
|
10
|
+
API_IS_CONNECTED,
|
|
11
|
+
API_MODE,
|
|
12
|
+
API_MODE_AVAIL,
|
|
13
|
+
API_SYSTEM_NUMBER,
|
|
14
|
+
AZD_CONNECTED,
|
|
15
|
+
AZD_ID,
|
|
16
|
+
AZD_INSTALLATION,
|
|
17
|
+
AZD_MODE,
|
|
18
|
+
AZD_MODES,
|
|
19
|
+
AZD_NAME,
|
|
20
|
+
AZD_SYSTEM,
|
|
21
|
+
AZD_WEBSERVER,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Device:
|
|
26
|
+
"""Airzone Cloud Device."""
|
|
27
|
+
|
|
28
|
+
connected: bool
|
|
29
|
+
name: str
|
|
30
|
+
|
|
31
|
+
def __init__(self, inst_id: str, ws_id: str, device_data: dict[str, Any]):
|
|
32
|
+
"""Airzone Cloud Device init."""
|
|
33
|
+
self.id = str(device_data[API_DEVICE_ID])
|
|
34
|
+
self.installation_id = inst_id
|
|
35
|
+
self.mode: OperationMode | None = None
|
|
36
|
+
self.modes: list[OperationMode] = []
|
|
37
|
+
self.system_number = int(device_data[API_CONFIG][API_SYSTEM_NUMBER])
|
|
38
|
+
self.webserver_id = ws_id
|
|
39
|
+
|
|
40
|
+
if API_IS_CONNECTED in device_data:
|
|
41
|
+
self.connected = bool(device_data[API_IS_CONNECTED])
|
|
42
|
+
|
|
43
|
+
def data(self) -> dict[str, Any]:
|
|
44
|
+
"""Return Device data."""
|
|
45
|
+
data = {
|
|
46
|
+
AZD_CONNECTED: self.get_connected(),
|
|
47
|
+
AZD_ID: self.get_id(),
|
|
48
|
+
AZD_INSTALLATION: self.get_installation(),
|
|
49
|
+
AZD_MODE: self.get_mode(),
|
|
50
|
+
AZD_NAME: self.get_name(),
|
|
51
|
+
AZD_SYSTEM: self.get_system(),
|
|
52
|
+
AZD_WEBSERVER: self.get_webserver(),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
modes = self.get_modes()
|
|
56
|
+
if modes:
|
|
57
|
+
data[AZD_MODES] = modes
|
|
58
|
+
|
|
59
|
+
return data
|
|
60
|
+
|
|
61
|
+
def get_connected(self) -> bool:
|
|
62
|
+
"""Return connected status."""
|
|
63
|
+
return self.connected
|
|
64
|
+
|
|
65
|
+
def get_id(self) -> str:
|
|
66
|
+
"""Return Device ID."""
|
|
67
|
+
return self.id
|
|
68
|
+
|
|
69
|
+
def get_installation(self) -> str:
|
|
70
|
+
"""Return Installation ID."""
|
|
71
|
+
return self.installation_id
|
|
72
|
+
|
|
73
|
+
def get_mode(self) -> OperationMode | None:
|
|
74
|
+
"""Return Device mode."""
|
|
75
|
+
return self.mode
|
|
76
|
+
|
|
77
|
+
def get_modes(self) -> list[OperationMode] | None:
|
|
78
|
+
"""Return Device modes."""
|
|
79
|
+
if len(self.modes) > 0:
|
|
80
|
+
return self.modes
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
def get_name(self) -> str:
|
|
84
|
+
"""Return Device name."""
|
|
85
|
+
return self.name
|
|
86
|
+
|
|
87
|
+
def get_system(self) -> int:
|
|
88
|
+
"""Return System number."""
|
|
89
|
+
return self.system_number
|
|
90
|
+
|
|
91
|
+
def get_webserver(self) -> str:
|
|
92
|
+
"""Return WebServer ID."""
|
|
93
|
+
return self.webserver_id
|
|
94
|
+
|
|
95
|
+
def update(self, data: dict[str, Any]) -> None:
|
|
96
|
+
"""Update Device data."""
|
|
97
|
+
if API_IS_CONNECTED in data:
|
|
98
|
+
self.connected = bool(data[API_IS_CONNECTED])
|
|
99
|
+
|
|
100
|
+
if API_MODE in data:
|
|
101
|
+
self.mode = OperationMode(data[API_MODE])
|
|
102
|
+
if API_MODE_AVAIL in data and len(data[API_MODE_AVAIL]) > 0:
|
|
103
|
+
modes = []
|
|
104
|
+
for mode in data[API_MODE_AVAIL]:
|
|
105
|
+
modes.append(OperationMode(mode))
|
|
106
|
+
self.modes = modes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Airzone Cloud API exceptions."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AirzoneCloudError(Exception):
|
|
6
|
+
"""Base class for Airzone Cloud errors."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class APIError(AirzoneCloudError):
|
|
10
|
+
"""Exception raised when API fails."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LoginError(AirzoneCloudError):
|
|
14
|
+
"""Exception raised when login fails."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TokenRefreshError(AirzoneCloudError):
|
|
18
|
+
"""Exception raised when token refresh fails."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InvalidParam(AirzoneCloudError):
|
|
22
|
+
"""Exception raised when invalid param is requested."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ParamUpdateFailure(AirzoneCloudError):
|
|
26
|
+
"""Exception raised when parameter isn't updated."""
|