plugwise 0.38.0__tar.gz → 0.38.2__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.
- {plugwise-0.38.0 → plugwise-0.38.2}/PKG-INFO +1 -1
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/__init__.py +50 -15
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/constants.py +2 -1
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/helper.py +41 -41
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/legacy/smile.py +18 -8
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/smile.py +21 -21
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise.egg-info/PKG-INFO +1 -1
- {plugwise-0.38.0 → plugwise-0.38.2}/pyproject.toml +1 -1
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_adam.py +32 -7
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_anna.py +19 -2
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_init.py +85 -21
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_legacy_anna.py +1 -1
- {plugwise-0.38.0 → plugwise-0.38.2}/LICENSE +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/README.md +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/common.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/data.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/exceptions.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/legacy/data.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/legacy/helper.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/py.typed +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise/util.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise.egg-info/SOURCES.txt +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise.egg-info/dependency_links.txt +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise.egg-info/requires.txt +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/plugwise.egg-info/top_level.txt +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/setup.cfg +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/setup.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_generic.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_legacy_generic.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_legacy_p1.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_legacy_stretch.py +0 -0
- {plugwise-0.38.0 → plugwise-0.38.2}/tests/test_p1.py +0 -0
@@ -5,6 +5,7 @@ Plugwise backend module for Home Assistant Core.
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
7
|
from plugwise.constants import (
|
8
|
+
DEFAULT_LEGACY_TIMEOUT,
|
8
9
|
DEFAULT_PORT,
|
9
10
|
DEFAULT_TIMEOUT,
|
10
11
|
DEFAULT_USERNAME,
|
@@ -46,7 +47,7 @@ class Smile(SmileComm):
|
|
46
47
|
websession: aiohttp.ClientSession,
|
47
48
|
username: str = DEFAULT_USERNAME,
|
48
49
|
port: int = DEFAULT_PORT,
|
49
|
-
timeout: float =
|
50
|
+
timeout: float = DEFAULT_LEGACY_TIMEOUT,
|
50
51
|
|
51
52
|
) -> None:
|
52
53
|
"""Set the constructor for this class."""
|
@@ -128,6 +129,7 @@ class Smile(SmileComm):
|
|
128
129
|
self._smile_api = SmileAPI(
|
129
130
|
self._host,
|
130
131
|
self._passwd,
|
132
|
+
self._timeout,
|
131
133
|
self._websession,
|
132
134
|
self._cooling_present,
|
133
135
|
self._elga,
|
@@ -147,10 +149,10 @@ class Smile(SmileComm):
|
|
147
149
|
self.smile_type,
|
148
150
|
self._user,
|
149
151
|
self._port,
|
150
|
-
self._timeout,
|
151
152
|
) if not self.smile_legacy else SmileLegacyAPI(
|
152
153
|
self._host,
|
153
154
|
self._passwd,
|
155
|
+
self._timeout,
|
154
156
|
self._websession,
|
155
157
|
self._is_thermostat,
|
156
158
|
self._on_off_device,
|
@@ -168,7 +170,6 @@ class Smile(SmileComm):
|
|
168
170
|
self.smile_zigbee_mac_address,
|
169
171
|
self._user,
|
170
172
|
self._port,
|
171
|
-
self._timeout,
|
172
173
|
)
|
173
174
|
|
174
175
|
# Update all endpoints on first connect
|
@@ -192,6 +193,9 @@ class Smile(SmileComm):
|
|
192
193
|
else:
|
193
194
|
model = await self._smile_detect_legacy(result, dsmrmain, model)
|
194
195
|
|
196
|
+
if not self.smile_legacy:
|
197
|
+
self._timeout = DEFAULT_TIMEOUT
|
198
|
+
|
195
199
|
if model == "Unknown" or self.smile_fw_version is None: # pragma: no cover
|
196
200
|
# Corner case check
|
197
201
|
LOGGER.error(
|
@@ -324,7 +328,10 @@ class Smile(SmileComm):
|
|
324
328
|
state: str | None = None,
|
325
329
|
) -> None:
|
326
330
|
"""Set the selected option for the applicable Select."""
|
327
|
-
|
331
|
+
try:
|
332
|
+
await self._smile_api.set_select(key, loc_id, option, state)
|
333
|
+
except ConnectionFailedError as exc:
|
334
|
+
raise ConnectionFailedError(f"Failed to set select option '{option}': {str(exc)}") from exc
|
328
335
|
|
329
336
|
async def set_schedule_state(
|
330
337
|
self,
|
@@ -333,15 +340,25 @@ class Smile(SmileComm):
|
|
333
340
|
name: str | None = None,
|
334
341
|
) -> None:
|
335
342
|
"""Activate/deactivate the Schedule, with the given name, on the relevant Thermostat."""
|
336
|
-
|
343
|
+
try:
|
344
|
+
await self._smile_api.set_schedule_state(loc_id, state, name)
|
345
|
+
except ConnectionFailedError as exc: # pragma no cover
|
346
|
+
raise ConnectionFailedError(f"Failed to set schedule state: {str(exc)}") from exc # pragma no cover
|
347
|
+
|
337
348
|
|
338
349
|
async def set_preset(self, loc_id: str, preset: str) -> None:
|
339
350
|
"""Set the given Preset on the relevant Thermostat."""
|
340
|
-
|
351
|
+
try:
|
352
|
+
await self._smile_api.set_preset(loc_id, preset)
|
353
|
+
except ConnectionFailedError as exc:
|
354
|
+
raise ConnectionFailedError(f"Failed to set preset: {str(exc)}") from exc
|
341
355
|
|
342
356
|
async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None:
|
343
357
|
"""Set the given Temperature on the relevant Thermostat."""
|
344
|
-
|
358
|
+
try:
|
359
|
+
await self._smile_api.set_temperature(loc_id, items)
|
360
|
+
except ConnectionFailedError as exc:
|
361
|
+
raise ConnectionFailedError(f"Failed to set temperature: {str(exc)}") from exc
|
345
362
|
|
346
363
|
async def set_number(
|
347
364
|
self,
|
@@ -350,40 +367,58 @@ class Smile(SmileComm):
|
|
350
367
|
temperature: float,
|
351
368
|
) -> None:
|
352
369
|
"""Set the maximum boiler- or DHW-setpoint on the Central Heating boiler or the temperature-offset on a Thermostat."""
|
353
|
-
|
370
|
+
try:
|
371
|
+
await self._smile_api.set_number(dev_id, key, temperature)
|
372
|
+
except ConnectionFailedError as exc:
|
373
|
+
raise ConnectionFailedError(f"Failed to set number '{key}': {str(exc)}") from exc
|
354
374
|
|
355
375
|
async def set_temperature_offset(self, dev_id: str, offset: float) -> None:
|
356
376
|
"""Set the Temperature offset for thermostats that support this feature."""
|
357
|
-
|
377
|
+
try: # pragma no cover
|
378
|
+
await self._smile_api.set_offset(dev_id, offset) # pragma: no cover
|
379
|
+
except ConnectionFailedError as exc: # pragma no cover
|
380
|
+
raise ConnectionFailedError(f"Failed to set temperature offset: {str(exc)}") from exc # pragma no cover
|
358
381
|
|
359
382
|
async def set_switch_state(
|
360
383
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
361
384
|
) -> None:
|
362
385
|
"""Set the given State of the relevant Switch."""
|
363
|
-
|
386
|
+
try:
|
387
|
+
await self._smile_api.set_switch_state(appl_id, members, model, state)
|
388
|
+
except ConnectionFailedError as exc:
|
389
|
+
raise ConnectionFailedError(f"Failed to set switch state: {str(exc)}") from exc
|
364
390
|
|
365
391
|
async def set_gateway_mode(self, mode: str) -> None:
|
366
392
|
"""Set the gateway mode."""
|
367
|
-
|
393
|
+
try: # pragma no cover
|
394
|
+
await self._smile_api.set_gateway_mode(mode) # pragma: no cover
|
395
|
+
except ConnectionFailedError as exc: # pragma no cover
|
396
|
+
raise ConnectionFailedError(f"Failed to set gateway mode: {str(exc)}") from exc # pragma no cover
|
368
397
|
|
369
398
|
async def set_regulation_mode(self, mode: str) -> None:
|
370
399
|
"""Set the heating regulation mode."""
|
371
|
-
|
400
|
+
try: # pragma no cover
|
401
|
+
await self._smile_api.set_regulation_mode(mode) # pragma: no cover
|
402
|
+
except ConnectionFailedError as exc: # pragma no cover
|
403
|
+
raise ConnectionFailedError(f"Failed to set regulation mode: {str(exc)}") from exc # pragma no cover
|
372
404
|
|
373
405
|
async def set_dhw_mode(self, mode: str) -> None:
|
374
406
|
"""Set the domestic hot water heating regulation mode."""
|
375
|
-
|
407
|
+
try: # pragma no cover
|
408
|
+
await self._smile_api.set_dhw_mode(mode) # pragma: no cover
|
409
|
+
except ConnectionFailedError as exc: # pragma no cover
|
410
|
+
raise ConnectionFailedError(f"Failed to set dhw mode: {str(exc)}") from exc # pragma no cover
|
376
411
|
|
377
412
|
async def delete_notification(self) -> None:
|
378
413
|
"""Delete the active Plugwise Notification."""
|
379
414
|
try:
|
380
415
|
await self._smile_api.delete_notification()
|
381
416
|
except ConnectionFailedError as exc:
|
382
|
-
raise
|
417
|
+
raise ConnectionFailedError(f"Failed to delete notification: {str(exc)}") from exc
|
383
418
|
|
384
419
|
async def reboot_gateway(self) -> None:
|
385
420
|
"""Reboot the Plugwise Gateway."""
|
386
421
|
try:
|
387
422
|
await self._smile_api.reboot_gateway()
|
388
423
|
except ConnectionFailedError as exc:
|
389
|
-
raise
|
424
|
+
raise ConnectionFailedError(f"Failed to reboot gateway: {str(exc)}") from exc
|
@@ -32,7 +32,8 @@ VOLUME_CUBIC_METERS_PER_HOUR: Final = "m³/h"
|
|
32
32
|
|
33
33
|
ADAM: Final = "Adam"
|
34
34
|
ANNA: Final = "Smile Anna"
|
35
|
-
DEFAULT_TIMEOUT: Final =
|
35
|
+
DEFAULT_TIMEOUT: Final = 10
|
36
|
+
DEFAULT_LEGACY_TIMEOUT: Final = 30
|
36
37
|
DEFAULT_USERNAME: Final = "smile"
|
37
38
|
DEFAULT_PORT: Final = 80
|
38
39
|
DEFAULT_PW_MAX: Final = 30.0
|
@@ -115,30 +115,31 @@ class SmileComm:
|
|
115
115
|
use_headers = headers
|
116
116
|
|
117
117
|
try:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
118
|
+
match method:
|
119
|
+
case "delete":
|
120
|
+
resp = await self._websession.delete(url, auth=self._auth)
|
121
|
+
case "get":
|
122
|
+
# Work-around for Stretchv2, should not hurt the other smiles
|
123
|
+
use_headers = {"Accept-Encoding": "gzip"}
|
124
|
+
resp = await self._websession.get(
|
125
|
+
url, headers=use_headers, auth=self._auth
|
126
|
+
)
|
127
|
+
case "post":
|
128
|
+
use_headers = {"Content-type": "text/xml"}
|
129
|
+
resp = await self._websession.post(
|
130
|
+
url,
|
131
|
+
headers=use_headers,
|
132
|
+
data=data,
|
133
|
+
auth=self._auth,
|
134
|
+
)
|
135
|
+
case "put":
|
136
|
+
use_headers = {"Content-type": "text/xml"}
|
137
|
+
resp = await self._websession.put(
|
138
|
+
url,
|
139
|
+
headers=use_headers,
|
140
|
+
data=data,
|
141
|
+
auth=self._auth,
|
142
|
+
)
|
142
143
|
except (
|
143
144
|
ClientError
|
144
145
|
) as exc: # ClientError is an ancestor class of ServerTimeoutError
|
@@ -167,23 +168,22 @@ class SmileComm:
|
|
167
168
|
|
168
169
|
async def _request_validate(self, resp: ClientResponse, method: str) -> etree:
|
169
170
|
"""Helper-function for _request(): validate the returned data."""
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
raise ConnectionFailedError
|
171
|
+
match resp.status:
|
172
|
+
case 200:
|
173
|
+
# Cornercases for server not responding with 202
|
174
|
+
if method in ("post", "put"):
|
175
|
+
return
|
176
|
+
case 202:
|
177
|
+
# Command accepted gives empty body with status 202
|
178
|
+
return
|
179
|
+
case 401:
|
180
|
+
msg = "Invalid Plugwise login, please retry with the correct credentials."
|
181
|
+
LOGGER.error("%s", msg)
|
182
|
+
raise InvalidAuthentication
|
183
|
+
case 405:
|
184
|
+
msg = "405 Method not allowed."
|
185
|
+
LOGGER.error("%s", msg)
|
186
|
+
raise ConnectionFailedError
|
187
187
|
|
188
188
|
if not (result := await resp.text()) or (
|
189
189
|
"<error>" in result and "Not started" not in result
|
@@ -5,11 +5,11 @@ Plugwise backend module for Home Assistant Core - covering the legacy P1, Anna,
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
7
|
import datetime as dt
|
8
|
+
from typing import Any
|
8
9
|
|
9
10
|
from plugwise.constants import (
|
10
11
|
APPLIANCES,
|
11
12
|
DEFAULT_PORT,
|
12
|
-
DEFAULT_TIMEOUT,
|
13
13
|
DEFAULT_USERNAME,
|
14
14
|
DOMAIN_OBJECTS,
|
15
15
|
LOCATIONS,
|
@@ -23,7 +23,7 @@ from plugwise.constants import (
|
|
23
23
|
PlugwiseData,
|
24
24
|
ThermoLoc,
|
25
25
|
)
|
26
|
-
from plugwise.exceptions import PlugwiseError
|
26
|
+
from plugwise.exceptions import ConnectionFailedError, PlugwiseError
|
27
27
|
from plugwise.helper import SmileComm
|
28
28
|
from plugwise.legacy.data import SmileLegacyData
|
29
29
|
|
@@ -40,6 +40,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
40
40
|
self,
|
41
41
|
host: str,
|
42
42
|
password: str,
|
43
|
+
timeout: float,
|
43
44
|
websession: aiohttp.ClientSession,
|
44
45
|
_is_thermostat: bool,
|
45
46
|
_on_off_device: bool,
|
@@ -57,7 +58,6 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
57
58
|
smile_zigbee_mac_address: str | None,
|
58
59
|
username: str = DEFAULT_USERNAME,
|
59
60
|
port: int = DEFAULT_PORT,
|
60
|
-
timeout: float = DEFAULT_TIMEOUT,
|
61
61
|
) -> None:
|
62
62
|
"""Set the constructor for this class."""
|
63
63
|
super().__init__(
|
@@ -76,6 +76,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
76
76
|
self._opentherm_device = _opentherm_device
|
77
77
|
self._stretch_v2 = _stretch_v2
|
78
78
|
self._target_smile = _target_smile
|
79
|
+
self._timeout = timeout
|
79
80
|
self.loc_data = loc_data
|
80
81
|
self.smile_fw_version = smile_fw_version
|
81
82
|
self.smile_hostname = smile_hostname
|
@@ -180,7 +181,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
180
181
|
rule = self._domain_objects.find(locator)
|
181
182
|
data = f'<rules><rule id="{rule.attrib["id"]}"><active>true</active></rule></rules>'
|
182
183
|
|
183
|
-
await self.
|
184
|
+
await self.call_request(RULES, method="put", data=data)
|
184
185
|
|
185
186
|
async def set_regulation_mode(self, mode: str) -> None:
|
186
187
|
"""Set-function placeholder for legacy devices."""
|
@@ -226,7 +227,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
226
227
|
f' id="{template_id}" /><active>{new_state}</active></rule></rules>'
|
227
228
|
)
|
228
229
|
|
229
|
-
await self.
|
230
|
+
await self.call_request(uri, method="put", data=data)
|
230
231
|
|
231
232
|
async def set_switch_state(
|
232
233
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
@@ -254,7 +255,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
254
255
|
if self._appliances.find(locator).text == "true":
|
255
256
|
raise PlugwiseError("Plugwise: the locked Relay was not switched.")
|
256
257
|
|
257
|
-
await self.
|
258
|
+
await self.call_request(uri, method="put", data=data)
|
258
259
|
|
259
260
|
async def _set_groupswitch_member_state(
|
260
261
|
self, members: list[str], state: str, switch: Munch
|
@@ -267,7 +268,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
267
268
|
uri = f"{APPLIANCES};id={member}/{switch.func_type}"
|
268
269
|
data = f"<{switch.func_type}><{switch.func}>{state}</{switch.func}></{switch.func_type}>"
|
269
270
|
|
270
|
-
await self.
|
271
|
+
await self.call_request(uri, method="put", data=data)
|
271
272
|
|
272
273
|
async def set_temperature(self, _: str, items: dict[str, float]) -> None:
|
273
274
|
"""Set the given Temperature on the relevant Thermostat."""
|
@@ -287,4 +288,13 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
287
288
|
f"{temperature}</setpoint></thermostat_functionality>"
|
288
289
|
)
|
289
290
|
|
290
|
-
await self.
|
291
|
+
await self.call_request(uri, method="put", data=data)
|
292
|
+
|
293
|
+
async def call_request(self, uri: str, **kwargs: Any) -> None:
|
294
|
+
"""ConnectionFailedError wrapper for calling _request()."""
|
295
|
+
method: str = kwargs["method"]
|
296
|
+
data: str | None = kwargs.get("data")
|
297
|
+
try:
|
298
|
+
await self._request(uri, method=method, data=data)
|
299
|
+
except ConnectionFailedError as exc:
|
300
|
+
raise ConnectionFailedError from exc
|
@@ -12,7 +12,6 @@ from plugwise.constants import (
|
|
12
12
|
ANNA,
|
13
13
|
APPLIANCES,
|
14
14
|
DEFAULT_PORT,
|
15
|
-
DEFAULT_TIMEOUT,
|
16
15
|
DEFAULT_USERNAME,
|
17
16
|
DOMAIN_OBJECTS,
|
18
17
|
GATEWAY_REBOOT,
|
@@ -47,6 +46,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
47
46
|
self,
|
48
47
|
host: str,
|
49
48
|
password: str,
|
49
|
+
timeout: float,
|
50
50
|
websession: aiohttp.ClientSession,
|
51
51
|
_cooling_present: bool,
|
52
52
|
_elga: bool,
|
@@ -66,8 +66,6 @@ class SmileAPI(SmileComm, SmileData):
|
|
66
66
|
smile_type: str,
|
67
67
|
username: str = DEFAULT_USERNAME,
|
68
68
|
port: int = DEFAULT_PORT,
|
69
|
-
timeout: float = DEFAULT_TIMEOUT,
|
70
|
-
|
71
69
|
) -> None:
|
72
70
|
"""Set the constructor for this class."""
|
73
71
|
super().__init__(
|
@@ -87,6 +85,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
87
85
|
self._on_off_device = _on_off_device
|
88
86
|
self._opentherm_device = _opentherm_device
|
89
87
|
self._schedule_old_states = _schedule_old_states
|
88
|
+
self._timeout = timeout
|
90
89
|
self.gateway_id = gateway_id
|
91
90
|
self.loc_data = loc_data
|
92
91
|
self.smile_fw_version = smile_fw_version
|
@@ -149,14 +148,6 @@ class SmileAPI(SmileComm, SmileData):
|
|
149
148
|
### API Set and HA Service-related Functions ###
|
150
149
|
########################################################################################################
|
151
150
|
|
152
|
-
async def call_request(self, uri: str, **kwargs: Any) -> None:
|
153
|
-
"""ConnectionFailedError wrapper for calling _request()."""
|
154
|
-
method: str = kwargs["method"]
|
155
|
-
try:
|
156
|
-
await self._request(uri, method=method)
|
157
|
-
except ConnectionFailedError as exc:
|
158
|
-
raise ConnectionFailedError from exc
|
159
|
-
|
160
151
|
async def delete_notification(self) -> None:
|
161
152
|
"""Delete the active Plugwise Notification."""
|
162
153
|
await self.call_request(NOTIFICATIONS, method="delete")
|
@@ -189,7 +180,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
189
180
|
|
190
181
|
uri = f"{APPLIANCES};id={self._heater_id}/thermostat;id={thermostat_id}"
|
191
182
|
data = f"<thermostat_functionality><setpoint>{temp}</setpoint></thermostat_functionality>"
|
192
|
-
await self.
|
183
|
+
await self.call_request(uri, method="put", data=data)
|
193
184
|
|
194
185
|
async def set_offset(self, dev_id: str, offset: float) -> None:
|
195
186
|
"""Set the Temperature offset for thermostats that support this feature."""
|
@@ -202,7 +193,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
202
193
|
uri = f"{APPLIANCES};id={dev_id}/offset;type=temperature_offset"
|
203
194
|
data = f"<offset_functionality><offset>{value}</offset></offset_functionality>"
|
204
195
|
|
205
|
-
await self.
|
196
|
+
await self.call_request(uri, method="put", data=data)
|
206
197
|
|
207
198
|
async def set_preset(self, loc_id: str, preset: str) -> None:
|
208
199
|
"""Set the given Preset on the relevant Thermostat - from LOCATIONS."""
|
@@ -222,7 +213,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
222
213
|
f"</type><preset>{preset}</preset></location></locations>"
|
223
214
|
)
|
224
215
|
|
225
|
-
await self.
|
216
|
+
await self.call_request(uri, method="put", data=data)
|
226
217
|
|
227
218
|
async def set_select(self, key: str, loc_id: str, option: str, state: str | None) -> None:
|
228
219
|
"""Set a dhw/gateway/regulation mode or the thermostat schedule option."""
|
@@ -245,7 +236,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
245
236
|
uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control"
|
246
237
|
data = f"<domestic_hot_water_mode_control_functionality><mode>{mode}</mode></domestic_hot_water_mode_control_functionality>"
|
247
238
|
|
248
|
-
await self.
|
239
|
+
await self.call_request(uri, method="put", data=data)
|
249
240
|
|
250
241
|
async def set_gateway_mode(self, mode: str) -> None:
|
251
242
|
"""Set the gateway mode."""
|
@@ -268,7 +259,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
268
259
|
uri = f"{APPLIANCES};id={self.gateway_id}/gateway_mode_control"
|
269
260
|
data = f"<gateway_mode_control_functionality><mode>{mode}</mode>{valid}</gateway_mode_control_functionality>"
|
270
261
|
|
271
|
-
await self.
|
262
|
+
await self.call_request(uri, method="put", data=data)
|
272
263
|
|
273
264
|
async def set_regulation_mode(self, mode: str) -> None:
|
274
265
|
"""Set the heating regulation mode."""
|
@@ -281,7 +272,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
281
272
|
duration = "<duration>300</duration>"
|
282
273
|
data = f"<regulation_mode_control_functionality>{duration}<mode>{mode}</mode></regulation_mode_control_functionality>"
|
283
274
|
|
284
|
-
await self.
|
275
|
+
await self.call_request(uri, method="put", data=data)
|
285
276
|
|
286
277
|
async def set_schedule_state(
|
287
278
|
self,
|
@@ -335,7 +326,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
335
326
|
f"{template}{contexts}</rule></rules>"
|
336
327
|
)
|
337
328
|
|
338
|
-
await self.
|
329
|
+
await self.call_request(uri, method="put", data=data)
|
339
330
|
self._schedule_old_states[loc_id][name] = new_state
|
340
331
|
|
341
332
|
def determine_contexts(
|
@@ -404,7 +395,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
404
395
|
if self._domain_objects.find(locator).text == "true":
|
405
396
|
raise PlugwiseError("Plugwise: the locked Relay was not switched.")
|
406
397
|
|
407
|
-
await self.
|
398
|
+
await self.call_request(uri, method="put", data=data)
|
408
399
|
|
409
400
|
async def _set_groupswitch_member_state(
|
410
401
|
self, members: list[str], state: str, switch: Munch
|
@@ -419,7 +410,7 @@ class SmileAPI(SmileComm, SmileData):
|
|
419
410
|
uri = f"{APPLIANCES};id={member}/{switch.device};id={switch_id}"
|
420
411
|
data = f"<{switch.func_type}><{switch.func}>{state}</{switch.func}></{switch.func_type}>"
|
421
412
|
|
422
|
-
await self.
|
413
|
+
await self.call_request(uri, method="put", data=data)
|
423
414
|
|
424
415
|
async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None:
|
425
416
|
"""Set the given Temperature on the relevant Thermostat."""
|
@@ -460,4 +451,13 @@ class SmileAPI(SmileComm, SmileData):
|
|
460
451
|
f"{temperature}</setpoint></thermostat_functionality>"
|
461
452
|
)
|
462
453
|
|
463
|
-
await self.
|
454
|
+
await self.call_request(uri, method="put", data=data)
|
455
|
+
|
456
|
+
async def call_request(self, uri: str, **kwargs: Any) -> None:
|
457
|
+
"""ConnectionFailedError wrapper for calling _request()."""
|
458
|
+
method: str = kwargs["method"]
|
459
|
+
data: str | None = kwargs.get("data")
|
460
|
+
try:
|
461
|
+
await self._request(uri, method=method, data=data)
|
462
|
+
except ConnectionFailedError as exc:
|
463
|
+
raise ConnectionFailedError from exc
|
@@ -63,7 +63,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
63
63
|
await self.disconnect(server, client)
|
64
64
|
|
65
65
|
server, smile, client = await self.connect_wrapper(raise_timeout=True)
|
66
|
-
await self.device_test(smile, "2022-05-16 00:00:01", testdata)
|
66
|
+
await self.device_test(smile, "2022-05-16 00:00:01", testdata, skip_testing=True)
|
67
67
|
result = await self.tinker_thermostat(
|
68
68
|
smile,
|
69
69
|
"c50f167537524366a5af7aa3942feb1e",
|
@@ -79,15 +79,18 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
79
79
|
)
|
80
80
|
assert result
|
81
81
|
|
82
|
+
tinkered = await self.tinker_max_boiler_temp(smile, unhappy=True)
|
83
|
+
assert not tinkered
|
84
|
+
|
82
85
|
try:
|
83
86
|
await smile.delete_notification()
|
84
87
|
notification_deletion = False # pragma: no cover
|
85
|
-
except pw_exceptions.
|
88
|
+
except pw_exceptions.ConnectionFailedError:
|
86
89
|
notification_deletion = True
|
87
90
|
assert notification_deletion
|
88
91
|
|
89
92
|
reboot = await self.tinker_reboot(smile, unhappy=True)
|
90
|
-
assert
|
93
|
+
assert reboot
|
91
94
|
|
92
95
|
await smile.close_connection()
|
93
96
|
await self.disconnect(server, client)
|
@@ -212,7 +215,7 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
212
215
|
await self.disconnect(server, client)
|
213
216
|
|
214
217
|
server, smile, client = await self.connect_wrapper(raise_timeout=True)
|
215
|
-
await self.device_test(smile, "2020-03-22 00:00:01", testdata)
|
218
|
+
await self.device_test(smile, "2020-03-22 00:00:01", testdata, skip_testing=True)
|
216
219
|
result = await self.tinker_thermostat(
|
217
220
|
smile,
|
218
221
|
"009490cc2f674ce6b576863fbb64f867",
|
@@ -322,9 +325,14 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
322
325
|
)
|
323
326
|
assert not switch_change
|
324
327
|
|
325
|
-
await self.tinker_gateway_mode(smile)
|
326
|
-
|
327
|
-
|
328
|
+
tinkered = await self.tinker_gateway_mode(smile)
|
329
|
+
assert not tinkered
|
330
|
+
|
331
|
+
tinkered = await self.tinker_regulation_mode(smile)
|
332
|
+
assert not tinkered
|
333
|
+
|
334
|
+
tinkered = await self.tinker_max_boiler_temp(smile)
|
335
|
+
assert not tinkered
|
328
336
|
|
329
337
|
# Now change some data and change directory reading xml from
|
330
338
|
# emulating reading newer dataset after an update_interval
|
@@ -353,6 +361,23 @@ class TestPlugwiseAdam(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
353
361
|
await smile.close_connection()
|
354
362
|
await self.disconnect(server, client)
|
355
363
|
|
364
|
+
self.smile_setup = "adam_plus_anna_new"
|
365
|
+
testdata = self.load_testdata(SMILE_TYPE, self.smile_setup)
|
366
|
+
server, smile, client = await self.connect_wrapper(raise_timeout=True)
|
367
|
+
await self.device_test(smile, "2023-12-17 00:00:01", testdata, skip_testing=True)
|
368
|
+
|
369
|
+
tinkered = await self.tinker_max_boiler_temp(smile, unhappy=True)
|
370
|
+
assert tinkered
|
371
|
+
|
372
|
+
tinkered = await self.tinker_gateway_mode(smile, unhappy=True)
|
373
|
+
assert tinkered
|
374
|
+
|
375
|
+
tinkered = await self.tinker_regulation_mode(smile, unhappy=True)
|
376
|
+
assert tinkered
|
377
|
+
|
378
|
+
await smile.close_connection()
|
379
|
+
await self.disconnect(server, client)
|
380
|
+
|
356
381
|
@pytest.mark.asyncio
|
357
382
|
async def test_adam_plus_jip(self):
|
358
383
|
"""Test Adam with Jip setup."""
|
@@ -70,7 +70,7 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
70
70
|
server, smile, client = await self.connect_wrapper(raise_timeout=True)
|
71
71
|
# Reset self.smile_setup
|
72
72
|
self.smile_setup = "anna_v4"
|
73
|
-
await self.device_test(smile, "2020-04-05 00:00:01", testdata)
|
73
|
+
await self.device_test(smile, "2020-04-05 00:00:01", testdata, skip_testing=True)
|
74
74
|
result = await self.tinker_thermostat(
|
75
75
|
smile,
|
76
76
|
"eb5309212bf5407bb143e5bfa3b18aee",
|
@@ -79,6 +79,12 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
79
79
|
unhappy=True,
|
80
80
|
)
|
81
81
|
assert result
|
82
|
+
|
83
|
+
result = await self.tinker_temp_offset(
|
84
|
+
smile, "01b85360fdd243d0aaad4d6ac2a5ba7e", unhappy=True,
|
85
|
+
)
|
86
|
+
assert result
|
87
|
+
|
82
88
|
await smile.close_connection()
|
83
89
|
await self.disconnect(server, client)
|
84
90
|
|
@@ -458,11 +464,22 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
458
464
|
"ERROR raised setting block cooling: %s", exc.value
|
459
465
|
) # pragma: no cover
|
460
466
|
|
461
|
-
await self.tinker_dhw_mode(smile)
|
467
|
+
tinkered = await self.tinker_dhw_mode(smile)
|
468
|
+
assert not tinkered
|
462
469
|
|
463
470
|
await smile.close_connection()
|
464
471
|
await self.disconnect(server, client)
|
465
472
|
|
473
|
+
server, smile, client = await self.connect_wrapper(raise_timeout=True)
|
474
|
+
await self.device_test(smile, "2022-05-16 00:00:01", testdata, skip_testing=True)
|
475
|
+
|
476
|
+
tinkered = await self.tinker_dhw_mode(smile, unhappy=True)
|
477
|
+
assert tinkered
|
478
|
+
|
479
|
+
await smile.close_connection()
|
480
|
+
await self.disconnect(server, client)
|
481
|
+
|
482
|
+
|
466
483
|
@pytest.mark.asyncio
|
467
484
|
async def test_connect_anna_loria_cooling_active(self):
|
468
485
|
"""Test an Anna with a Loria in heating mode - state idle."""
|
@@ -536,6 +536,7 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
536
536
|
test_time=None,
|
537
537
|
testdata=None,
|
538
538
|
initialize=True,
|
539
|
+
skip_testing=False,
|
539
540
|
):
|
540
541
|
"""Perform basic device tests."""
|
541
542
|
bsw_list = ["binary_sensors", "central", "climate", "sensors", "switches"]
|
@@ -551,8 +552,10 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
551
552
|
await smile.full_update_device()
|
552
553
|
smile.get_all_devices()
|
553
554
|
data = await smile.async_update()
|
555
|
+
assert smile._timeout == 30
|
554
556
|
else:
|
555
557
|
data = await smile.async_update()
|
558
|
+
assert smile._timeout == 10
|
556
559
|
else:
|
557
560
|
_LOGGER.info("Asserting updated testdata:")
|
558
561
|
data = await smile.async_update()
|
@@ -588,6 +591,9 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
588
591
|
_LOGGER.info("Device list = %s", data.devices)
|
589
592
|
self.show_setup(location_list, data.devices)
|
590
593
|
|
594
|
+
if skip_testing:
|
595
|
+
return
|
596
|
+
|
591
597
|
# Perform tests and asserts
|
592
598
|
tests = 0
|
593
599
|
asserts = 0
|
@@ -644,10 +650,10 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
644
650
|
await smile.reboot_gateway()
|
645
651
|
_LOGGER.info(" + worked as intended")
|
646
652
|
return True
|
647
|
-
except pw_exceptions.
|
653
|
+
except pw_exceptions.ConnectionFailedError:
|
648
654
|
if unhappy:
|
649
655
|
_LOGGER.info(" + failed as expected")
|
650
|
-
return
|
656
|
+
return True
|
651
657
|
else: # pragma: no cover
|
652
658
|
_LOGGER.info(" - failed unexpectedly")
|
653
659
|
return False
|
@@ -659,8 +665,8 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
659
665
|
"""Turn a Switch on and off to test functionality."""
|
660
666
|
_LOGGER.info("Asserting modifying settings for switch devices:")
|
661
667
|
_LOGGER.info("- Devices (%s):", dev_id)
|
668
|
+
tinker_switch_passed = False
|
662
669
|
for new_state in ["false", "true", "false"]:
|
663
|
-
tinker_switch_passed = False
|
664
670
|
_LOGGER.info("- Switching %s", new_state)
|
665
671
|
try:
|
666
672
|
await smile.set_switch_state(dev_id, members, model, new_state)
|
@@ -669,9 +675,9 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
669
675
|
except pw_exceptions.PlugwiseError:
|
670
676
|
_LOGGER.info(" + locked, not switched as expected")
|
671
677
|
return False
|
672
|
-
except pw_exceptions.ConnectionFailedError:
|
678
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
673
679
|
if unhappy:
|
674
|
-
|
680
|
+
return True # test is pass!
|
675
681
|
_LOGGER.info(" + failed as expected")
|
676
682
|
else: # pragma: no cover
|
677
683
|
_LOGGER.info(" - failed unexpectedly")
|
@@ -697,18 +703,18 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
697
703
|
except pw_exceptions.ConnectionFailedError:
|
698
704
|
if unhappy:
|
699
705
|
_LOGGER.info(" + tinker_thermostat_temp failed as expected")
|
700
|
-
|
706
|
+
return True
|
701
707
|
else: # pragma: no cover
|
702
708
|
_LOGGER.info(" - tinker_thermostat_temp failed unexpectedly")
|
703
|
-
|
709
|
+
return False
|
704
710
|
|
705
711
|
return tinker_temp_passed
|
706
712
|
|
707
713
|
@pytest.mark.asyncio
|
708
714
|
async def tinker_thermostat_preset(self, smile, loc_id, unhappy=False):
|
709
715
|
"""Toggle preset to test functionality."""
|
716
|
+
tinker_preset_passed = False
|
710
717
|
for new_preset in ["asleep", "home", BOGUS]:
|
711
|
-
tinker_preset_passed = False
|
712
718
|
warning = ""
|
713
719
|
if new_preset[0] == "!":
|
714
720
|
warning = " TTP Negative test"
|
@@ -721,10 +727,10 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
721
727
|
except pw_exceptions.PlugwiseError:
|
722
728
|
_LOGGER.info(" + found invalid preset, as expected")
|
723
729
|
tinker_preset_passed = True
|
724
|
-
except pw_exceptions.ConnectionFailedError:
|
730
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
725
731
|
if unhappy:
|
726
|
-
tinker_preset_passed = True
|
727
732
|
_LOGGER.info(" + tinker_thermostat_preset failed as expected")
|
733
|
+
return True
|
728
734
|
else: # pragma: no cover
|
729
735
|
_LOGGER.info(" - tinker_thermostat_preset failed unexpectedly")
|
730
736
|
return False
|
@@ -740,8 +746,9 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
740
746
|
if good_schedules != []:
|
741
747
|
if not single and ("!VeryBogusSchedule" not in good_schedules):
|
742
748
|
good_schedules.append("!VeryBogusSchedule")
|
749
|
+
|
750
|
+
tinker_schedule_passed = False
|
743
751
|
for new_schedule in good_schedules:
|
744
|
-
tinker_schedule_passed = False
|
745
752
|
warning = ""
|
746
753
|
if new_schedule is not None and new_schedule[0] == "!":
|
747
754
|
warning = " TTS Negative test"
|
@@ -754,11 +761,11 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
754
761
|
except pw_exceptions.PlugwiseError:
|
755
762
|
_LOGGER.info(" + failed as expected")
|
756
763
|
tinker_schedule_passed = True
|
757
|
-
except pw_exceptions.ConnectionFailedError:
|
764
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
758
765
|
tinker_schedule_passed = False
|
759
766
|
if unhappy:
|
760
767
|
_LOGGER.info(" + failed as expected before intended failure")
|
761
|
-
|
768
|
+
return True
|
762
769
|
else: # pragma: no cover
|
763
770
|
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
764
771
|
return False
|
@@ -772,6 +779,7 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
772
779
|
async def tinker_legacy_thermostat_schedule(self, smile, unhappy=False):
|
773
780
|
"""Toggle schedules to test functionality."""
|
774
781
|
states = ["on", "off", "!Bogus"]
|
782
|
+
tinker_schedule_passed = False
|
775
783
|
for state in states:
|
776
784
|
_LOGGER.info("- Adjusting schedule to state %s", state)
|
777
785
|
try:
|
@@ -781,11 +789,11 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
781
789
|
except pw_exceptions.PlugwiseError:
|
782
790
|
_LOGGER.info(" + failed as expected")
|
783
791
|
tinker_schedule_passed = True
|
784
|
-
except pw_exceptions.ConnectionFailedError:
|
792
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
785
793
|
tinker_schedule_passed = False
|
786
794
|
if unhappy:
|
787
795
|
_LOGGER.info(" + failed as expected before intended failure")
|
788
|
-
|
796
|
+
return True
|
789
797
|
else: # pragma: no cover
|
790
798
|
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
791
799
|
return False
|
@@ -833,7 +841,7 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
833
841
|
smile,
|
834
842
|
schedule_on=True,
|
835
843
|
block_cooling=False,
|
836
|
-
unhappy=False
|
844
|
+
unhappy=False,
|
837
845
|
):
|
838
846
|
"""Toggle various climate settings to test functionality."""
|
839
847
|
result_1 = await self.tinker_thermostat_temp(
|
@@ -847,8 +855,9 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
847
855
|
return result_1 and result_2 and result_3
|
848
856
|
|
849
857
|
@staticmethod
|
850
|
-
async def tinker_dhw_mode(smile):
|
858
|
+
async def tinker_dhw_mode(smile, unhappy=False):
|
851
859
|
"""Toggle dhw to test functionality."""
|
860
|
+
tinker_dhw_mode_passed = False
|
852
861
|
for mode in ["auto", "boost", BOGUS]:
|
853
862
|
warning = ""
|
854
863
|
if mode[0] == "!":
|
@@ -858,12 +867,24 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
858
867
|
try:
|
859
868
|
await smile.set_select("select_dhw_mode", "dummy", mode)
|
860
869
|
_LOGGER.info(" + tinker_dhw_mode worked as intended")
|
870
|
+
tinker_dhw_mode_passed = True
|
861
871
|
except pw_exceptions.PlugwiseError:
|
862
872
|
_LOGGER.info(" + tinker_dhw_mode found invalid mode, as expected")
|
873
|
+
tinker_dhw_mode_passed = False
|
874
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
875
|
+
if unhappy:
|
876
|
+
_LOGGER.info(" + failed as expected before intended failure")
|
877
|
+
return True
|
878
|
+
else: # pragma: no cover
|
879
|
+
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
880
|
+
return False
|
881
|
+
|
882
|
+
return tinker_dhw_mode_passed
|
863
883
|
|
864
884
|
@staticmethod
|
865
|
-
async def tinker_regulation_mode(smile):
|
885
|
+
async def tinker_regulation_mode(smile, unhappy=False):
|
866
886
|
"""Toggle regulation_mode to test functionality."""
|
887
|
+
tinker_reg_mode_passed = False
|
867
888
|
for mode in ["off", "heating", "bleeding_cold", BOGUS]:
|
868
889
|
warning = ""
|
869
890
|
if mode[0] == "!":
|
@@ -873,25 +894,49 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
873
894
|
try:
|
874
895
|
await smile.set_select("select_regulation_mode", "dummy", mode)
|
875
896
|
_LOGGER.info(" + tinker_regulation_mode worked as intended")
|
897
|
+
tinker_reg_mode_passed = True
|
876
898
|
except pw_exceptions.PlugwiseError:
|
877
899
|
_LOGGER.info(
|
878
900
|
" + tinker_regulation_mode found invalid mode, as expected"
|
879
901
|
)
|
902
|
+
tinker_reg_mode_passed = False
|
903
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
904
|
+
if unhappy:
|
905
|
+
_LOGGER.info(" + failed as expected before intended failure")
|
906
|
+
return True
|
907
|
+
else: # pragma: no cover
|
908
|
+
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
909
|
+
return False
|
910
|
+
|
911
|
+
return tinker_reg_mode_passed
|
880
912
|
|
881
913
|
@staticmethod
|
882
|
-
async def tinker_max_boiler_temp(smile):
|
914
|
+
async def tinker_max_boiler_temp(smile, unhappy=False):
|
883
915
|
"""Change max boiler temp setpoint to test functionality."""
|
916
|
+
tinker_max_boiler_temp_passed = False
|
884
917
|
new_temp = 60.0
|
885
918
|
_LOGGER.info("- Adjusting temperature to %s", new_temp)
|
886
919
|
for test in ["maximum_boiler_temperature", "bogus_temperature"]:
|
920
|
+
_LOGGER.info(" + for %s", test)
|
887
921
|
try:
|
888
922
|
await smile.set_number("dummy", test, new_temp)
|
889
923
|
_LOGGER.info(" + tinker_max_boiler_temp worked as intended")
|
924
|
+
tinker_max_boiler_temp_passed = True
|
890
925
|
except pw_exceptions.PlugwiseError:
|
891
926
|
_LOGGER.info(" + tinker_max_boiler_temp failed as intended")
|
927
|
+
tinker_max_boiler_temp_passed = False
|
928
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
929
|
+
if unhappy:
|
930
|
+
_LOGGER.info(" + failed as expected before intended failure")
|
931
|
+
return True
|
932
|
+
else: # pragma: no cover
|
933
|
+
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
934
|
+
return False
|
935
|
+
|
936
|
+
return tinker_max_boiler_temp_passed
|
892
937
|
|
893
938
|
@staticmethod
|
894
|
-
async def tinker_temp_offset(smile, dev_id):
|
939
|
+
async def tinker_temp_offset(smile, dev_id, unhappy=False):
|
895
940
|
"""Change temperature_offset to test functionality."""
|
896
941
|
new_offset = 1.0
|
897
942
|
_LOGGER.info("- Adjusting temperature offset to %s", new_offset)
|
@@ -902,10 +947,18 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
902
947
|
except pw_exceptions.PlugwiseError:
|
903
948
|
_LOGGER.info(" + tinker_temp_offset failed as intended")
|
904
949
|
return False
|
950
|
+
except pw_exceptions.ConnectionFailedError:
|
951
|
+
if unhappy:
|
952
|
+
_LOGGER.info(" + failed as expected before intended failure")
|
953
|
+
return True
|
954
|
+
else: # pragma: no cover
|
955
|
+
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
956
|
+
return False
|
905
957
|
|
906
958
|
@staticmethod
|
907
|
-
async def tinker_gateway_mode(smile):
|
959
|
+
async def tinker_gateway_mode(smile, unhappy=False):
|
908
960
|
"""Toggle gateway_mode to test functionality."""
|
961
|
+
tinker_gateway_mode_passed = False
|
909
962
|
for mode in ["away", "full", "vacation", "!bogus"]:
|
910
963
|
warning = ""
|
911
964
|
if mode[0] == "!":
|
@@ -915,8 +968,19 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init
|
|
915
968
|
try:
|
916
969
|
await smile.set_select("select_gateway_mode", "dummy", mode)
|
917
970
|
_LOGGER.info(" + worked as intended")
|
971
|
+
tinker_gateway_mode_passed = True
|
918
972
|
except pw_exceptions.PlugwiseError:
|
919
973
|
_LOGGER.info(" + found invalid mode, as expected")
|
974
|
+
tinker_gateway_mode_passed = False
|
975
|
+
except pw_exceptions.ConnectionFailedError: # leave for-loop at connect-error
|
976
|
+
if unhappy:
|
977
|
+
_LOGGER.info(" + failed as expected before intended failure")
|
978
|
+
return True
|
979
|
+
else: # pragma: no cover
|
980
|
+
_LOGGER.info(" - succeeded unexpectedly for some reason")
|
981
|
+
return False
|
982
|
+
|
983
|
+
return tinker_gateway_mode_passed
|
920
984
|
|
921
985
|
@staticmethod
|
922
986
|
def validate_test_basics(
|
@@ -39,7 +39,7 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi
|
|
39
39
|
await self.disconnect(server, client)
|
40
40
|
|
41
41
|
server, smile, client = await self.connect_legacy_wrapper(raise_timeout=True)
|
42
|
-
await self.device_test(smile, "2020-03-22 00:00:01", testdata)
|
42
|
+
await self.device_test(smile, "2020-03-22 00:00:01", testdata, skip_testing=True)
|
43
43
|
result = await self.tinker_legacy_thermostat(smile, unhappy=True)
|
44
44
|
assert result
|
45
45
|
await smile.close_connection()
|
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
|