plugwise 0.37.9__tar.gz → 0.38.1__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.37.9 → plugwise-0.38.1}/PKG-INFO +9 -8
- {plugwise-0.37.9 → plugwise-0.38.1}/README.md +8 -7
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/__init__.py +91 -40
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/constants.py +2 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/data.py +1 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/exceptions.py +4 -8
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/helper.py +56 -30
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/legacy/smile.py +19 -6
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/smile.py +36 -19
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise.egg-info/PKG-INFO +9 -8
- {plugwise-0.37.9 → plugwise-0.38.1}/pyproject.toml +2 -2
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_adam.py +52 -7
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_anna.py +19 -2
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_init.py +112 -41
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_legacy_anna.py +1 -1
- {plugwise-0.37.9 → plugwise-0.38.1}/LICENSE +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/common.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/legacy/data.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/legacy/helper.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/py.typed +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise/util.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise.egg-info/SOURCES.txt +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise.egg-info/dependency_links.txt +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise.egg-info/requires.txt +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/plugwise.egg-info/top_level.txt +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/setup.cfg +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/setup.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_generic.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_legacy_generic.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_legacy_p1.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_legacy_stretch.py +0 -0
- {plugwise-0.37.9 → plugwise-0.38.1}/tests/test_p1.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: plugwise
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.38.1
|
4
4
|
Summary: Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3.
|
5
5
|
Home-page: https://github.com/plugwise/python-plugwise
|
6
6
|
Author: Plugwise device owners
|
@@ -54,12 +54,13 @@ This module supports `Smile`s (and `Stretch`), i.e. the networked plugwise devic
|
|
54
54
|
Our main usage for this module is supporting [Home Assistant](https://www.home-assistant.io) / [home-assistant](http://github.com/home-assistant/core/)
|
55
55
|
|
56
56
|
[](https://github.com/plugwise)
|
57
|
-
[](https://coderabbit.ai)
|
58
|
+
[](https://github.com/plugwise/python-plugwise/issues/291)
|
58
59
|
|
60
|
+
[](https://pypi.python.org/pypi/plugwise/)
|
59
61
|
[](https://github.com/plugwise/python-plugwise/actions)
|
60
62
|
[](https://github.com/plugwise/python-plugwise/actions)
|
61
63
|
[](https://results.pre-commit.ci/latest/github/plugwise/python-plugwise/main)
|
62
|
-
[](https://github.com/plugwise/python-plugwise/issues/291)
|
63
64
|
|
64
65
|
[](https://www.codefactor.io/repository/github/plugwise/python-plugwise)
|
65
66
|
[](https://codecov.io/gh/plugwise/python-plugwise)
|
@@ -94,9 +95,9 @@ See the [`plugwise-beta`](https://github.com/plugwise/plugwise-beta) repository
|
|
94
95
|
|
95
96
|
## Development/patches
|
96
97
|
|
97
|
-
Like Home Assistant Core we use `pre-commit` and additionally run [pre-commit.ci](http://pre-commit.ci) to automatically validate your commits and PRs.
|
98
|
+
Like Home Assistant Core, we use `pre-commit` and additionally run [pre-commit.ci](http://pre-commit.ci) to automatically validate your commits and PRs.
|
98
99
|
|
99
|
-
If you want to create a PR please
|
100
|
+
If you want to create a PR, please ensure you at least run `scripts/setup.sh`. This will ensure your environment is set up correctly before attempting to `git commit`. We sincerely and highly recommended also setting up local testing, see [`tests/README.md`](https://github.com/plugwise/python-plugwise/blob/main/tests/README.md) for more information and run `scripts/setup_test.sh` to prepare your environment.
|
100
101
|
|
101
102
|
## Project support status
|
102
103
|
|
@@ -125,13 +126,13 @@ Module providing interfacing with the Plugwise devices:
|
|
125
126
|
|
126
127
|
## License, origins and contributors
|
127
128
|
|
128
|
-
As per the origins we have retained the appropriate licensing and including the MIT-license for this project.
|
129
|
+
As per the origins, we have retained the appropriate licensing and including the MIT-license for this project.
|
129
130
|
|
130
131
|
Origins (from newest to oldest):
|
131
132
|
|
132
133
|
- Networked (Smile/Stretch) Plugwise support by @bouwew (Bouwe) and @CoMPaTech (Tom). We both support and help out @brefra (Frank) where possible, he's supporting the USB module and integration.
|
133
134
|
- 'All' available Plugwise support by @bouwew (Bouwe), @brefra (Frank) and @CoMPaTech (Tom)
|
134
|
-
- Upstreamed haanna/HA-core Anna, including all later products - 'Plugwise-Smile/Plugwise-HA/plugwise-beta
|
135
|
+
- Upstreamed haanna/HA-core Anna, including all later products - 'Plugwise-Smile/Plugwise-HA/plugwise-beta' by @bouwew (Bouwe) & @CoMPaTech (Tom)
|
135
136
|
- Networked Plugwise Anna module with custom_module - `haanna/anna-ha` via <https://github.com/laetificat> (Kevin)
|
136
137
|
- USB-based stick module with custom_module - `plugwise-stick/plugwise` by @brefra (Frank)
|
137
138
|
- USB-plugwise module - `plugwise` by <https://github.com/cyberjunky/python-plugwise> (Ron) originally by <https://github.com/aequitas/python-plugwise> (Johan) (with reference only in license to Sven)
|
@@ -139,4 +140,4 @@ Origins (from newest to oldest):
|
|
139
140
|
|
140
141
|
## Thanks
|
141
142
|
|
142
|
-
On behalf all of us, big thanks to Plugwise and community members @riemers and @tane from [HAshop](https://hashop.nl) for their support and obviously all our users and testers who dealt with our typos and challenges. Disclaimer, while we are communicating with Plugwise and they expressed their gratitude through their newsletter, we are not part of Plugwise as a company. We are just a bunch of guys anxious to get our (and your) Plugwise products working with Home Assistant.
|
143
|
+
On behalf of all of us, big thanks to Plugwise and community members @riemers and @tane from [HAshop](https://hashop.nl) for their support and obviously all our users and testers who dealt with our typos and challenges. Disclaimer, while we are communicating with Plugwise and they expressed their gratitude through their newsletter, we are not part of Plugwise as a company. We are just a bunch of guys anxious to get our (and your) Plugwise products working with Home Assistant.
|
@@ -7,12 +7,13 @@ This module supports `Smile`s (and `Stretch`), i.e. the networked plugwise devic
|
|
7
7
|
Our main usage for this module is supporting [Home Assistant](https://www.home-assistant.io) / [home-assistant](http://github.com/home-assistant/core/)
|
8
8
|
|
9
9
|
[](https://github.com/plugwise)
|
10
|
-
[](https://coderabbit.ai)
|
11
|
+
[](https://github.com/plugwise/python-plugwise/issues/291)
|
11
12
|
|
13
|
+
[](https://pypi.python.org/pypi/plugwise/)
|
12
14
|
[](https://github.com/plugwise/python-plugwise/actions)
|
13
15
|
[](https://github.com/plugwise/python-plugwise/actions)
|
14
16
|
[](https://results.pre-commit.ci/latest/github/plugwise/python-plugwise/main)
|
15
|
-
[](https://github.com/plugwise/python-plugwise/issues/291)
|
16
17
|
|
17
18
|
[](https://www.codefactor.io/repository/github/plugwise/python-plugwise)
|
18
19
|
[](https://codecov.io/gh/plugwise/python-plugwise)
|
@@ -47,9 +48,9 @@ See the [`plugwise-beta`](https://github.com/plugwise/plugwise-beta) repository
|
|
47
48
|
|
48
49
|
## Development/patches
|
49
50
|
|
50
|
-
Like Home Assistant Core we use `pre-commit` and additionally run [pre-commit.ci](http://pre-commit.ci) to automatically validate your commits and PRs.
|
51
|
+
Like Home Assistant Core, we use `pre-commit` and additionally run [pre-commit.ci](http://pre-commit.ci) to automatically validate your commits and PRs.
|
51
52
|
|
52
|
-
If you want to create a PR please
|
53
|
+
If you want to create a PR, please ensure you at least run `scripts/setup.sh`. This will ensure your environment is set up correctly before attempting to `git commit`. We sincerely and highly recommended also setting up local testing, see [`tests/README.md`](https://github.com/plugwise/python-plugwise/blob/main/tests/README.md) for more information and run `scripts/setup_test.sh` to prepare your environment.
|
53
54
|
|
54
55
|
## Project support status
|
55
56
|
|
@@ -78,13 +79,13 @@ Module providing interfacing with the Plugwise devices:
|
|
78
79
|
|
79
80
|
## License, origins and contributors
|
80
81
|
|
81
|
-
As per the origins we have retained the appropriate licensing and including the MIT-license for this project.
|
82
|
+
As per the origins, we have retained the appropriate licensing and including the MIT-license for this project.
|
82
83
|
|
83
84
|
Origins (from newest to oldest):
|
84
85
|
|
85
86
|
- Networked (Smile/Stretch) Plugwise support by @bouwew (Bouwe) and @CoMPaTech (Tom). We both support and help out @brefra (Frank) where possible, he's supporting the USB module and integration.
|
86
87
|
- 'All' available Plugwise support by @bouwew (Bouwe), @brefra (Frank) and @CoMPaTech (Tom)
|
87
|
-
- Upstreamed haanna/HA-core Anna, including all later products - 'Plugwise-Smile/Plugwise-HA/plugwise-beta
|
88
|
+
- Upstreamed haanna/HA-core Anna, including all later products - 'Plugwise-Smile/Plugwise-HA/plugwise-beta' by @bouwew (Bouwe) & @CoMPaTech (Tom)
|
88
89
|
- Networked Plugwise Anna module with custom_module - `haanna/anna-ha` via <https://github.com/laetificat> (Kevin)
|
89
90
|
- USB-based stick module with custom_module - `plugwise-stick/plugwise` by @brefra (Frank)
|
90
91
|
- USB-plugwise module - `plugwise` by <https://github.com/cyberjunky/python-plugwise> (Ron) originally by <https://github.com/aequitas/python-plugwise> (Johan) (with reference only in license to Sven)
|
@@ -92,4 +93,4 @@ Origins (from newest to oldest):
|
|
92
93
|
|
93
94
|
## Thanks
|
94
95
|
|
95
|
-
On behalf all of us, big thanks to Plugwise and community members @riemers and @tane from [HAshop](https://hashop.nl) for their support and obviously all our users and testers who dealt with our typos and challenges. Disclaimer, while we are communicating with Plugwise and they expressed their gratitude through their newsletter, we are not part of Plugwise as a company. We are just a bunch of guys anxious to get our (and your) Plugwise products working with Home Assistant.
|
96
|
+
On behalf of all of us, big thanks to Plugwise and community members @riemers and @tane from [HAshop](https://hashop.nl) for their support and obviously all our users and testers who dealt with our typos and challenges. Disclaimer, while we are communicating with Plugwise and they expressed their gratitude through their newsletter, we are not part of Plugwise as a company. We are just a bunch of guys anxious to get our (and your) Plugwise products working with Home Assistant.
|
@@ -18,7 +18,14 @@ from plugwise.constants import (
|
|
18
18
|
PlugwiseData,
|
19
19
|
ThermoLoc,
|
20
20
|
)
|
21
|
-
from plugwise.exceptions import
|
21
|
+
from plugwise.exceptions import (
|
22
|
+
ConnectionFailedError,
|
23
|
+
DataMissingError,
|
24
|
+
InvalidSetupError,
|
25
|
+
PlugwiseError,
|
26
|
+
ResponseError,
|
27
|
+
UnsupportedDeviceError,
|
28
|
+
)
|
22
29
|
from plugwise.helper import SmileComm
|
23
30
|
from plugwise.legacy.smile import SmileLegacyAPI
|
24
31
|
from plugwise.smile import SmileAPI
|
@@ -141,30 +148,28 @@ class Smile(SmileComm):
|
|
141
148
|
self._user,
|
142
149
|
self._port,
|
143
150
|
self._timeout,
|
144
|
-
)
|
145
|
-
|
146
|
-
self.
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
self._timeout,
|
167
|
-
)
|
151
|
+
) if not self.smile_legacy else SmileLegacyAPI(
|
152
|
+
self._host,
|
153
|
+
self._passwd,
|
154
|
+
self._websession,
|
155
|
+
self._is_thermostat,
|
156
|
+
self._on_off_device,
|
157
|
+
self._opentherm_device,
|
158
|
+
self._stretch_v2,
|
159
|
+
self._target_smile,
|
160
|
+
self.loc_data,
|
161
|
+
self.smile_fw_version,
|
162
|
+
self.smile_hostname,
|
163
|
+
self.smile_hw_version,
|
164
|
+
self.smile_mac_address,
|
165
|
+
self.smile_model,
|
166
|
+
self.smile_name,
|
167
|
+
self.smile_type,
|
168
|
+
self.smile_zigbee_mac_address,
|
169
|
+
self._user,
|
170
|
+
self._port,
|
171
|
+
self._timeout,
|
172
|
+
)
|
168
173
|
|
169
174
|
# Update all endpoints on first connect
|
170
175
|
await self._smile_api.full_update_device()
|
@@ -289,17 +294,22 @@ class Smile(SmileComm):
|
|
289
294
|
return return_model
|
290
295
|
|
291
296
|
async def full_update_device(self) -> None:
|
292
|
-
"""
|
297
|
+
"""Helper-function used for testing."""
|
293
298
|
await self._smile_api.full_update_device()
|
294
299
|
|
295
300
|
def get_all_devices(self) -> None:
|
296
|
-
"""
|
301
|
+
"""Helper-function used for testing."""
|
297
302
|
self._smile_api.get_all_devices()
|
298
303
|
|
299
304
|
async def async_update(self) -> PlugwiseData:
|
300
305
|
"""Perform an incremental update for updating the various device states."""
|
301
|
-
data
|
302
|
-
|
306
|
+
data = PlugwiseData({}, {})
|
307
|
+
try:
|
308
|
+
data = await self._smile_api.async_update()
|
309
|
+
self.gateway_id = data.gateway["gateway_id"]
|
310
|
+
except (DataMissingError, KeyError) as err:
|
311
|
+
raise PlugwiseError("No Plugwise data received") from err
|
312
|
+
|
303
313
|
return data
|
304
314
|
|
305
315
|
########################################################################################################
|
@@ -314,7 +324,10 @@ class Smile(SmileComm):
|
|
314
324
|
state: str | None = None,
|
315
325
|
) -> None:
|
316
326
|
"""Set the selected option for the applicable Select."""
|
317
|
-
|
327
|
+
try:
|
328
|
+
await self._smile_api.set_select(key, loc_id, option, state)
|
329
|
+
except ConnectionFailedError as exc:
|
330
|
+
raise ConnectionFailedError(f"Failed to set select option '{option}': {str(exc)}") from exc
|
318
331
|
|
319
332
|
async def set_schedule_state(
|
320
333
|
self,
|
@@ -323,15 +336,25 @@ class Smile(SmileComm):
|
|
323
336
|
name: str | None = None,
|
324
337
|
) -> None:
|
325
338
|
"""Activate/deactivate the Schedule, with the given name, on the relevant Thermostat."""
|
326
|
-
|
339
|
+
try:
|
340
|
+
await self._smile_api.set_schedule_state(loc_id, state, name)
|
341
|
+
except ConnectionFailedError as exc: # pragma no cover
|
342
|
+
raise ConnectionFailedError(f"Failed to set schedule state: {str(exc)}") from exc # pragma no cover
|
343
|
+
|
327
344
|
|
328
345
|
async def set_preset(self, loc_id: str, preset: str) -> None:
|
329
346
|
"""Set the given Preset on the relevant Thermostat."""
|
330
|
-
|
347
|
+
try:
|
348
|
+
await self._smile_api.set_preset(loc_id, preset)
|
349
|
+
except ConnectionFailedError as exc:
|
350
|
+
raise ConnectionFailedError(f"Failed to set preset: {str(exc)}") from exc
|
331
351
|
|
332
352
|
async def set_temperature(self, loc_id: str, items: dict[str, float]) -> None:
|
333
353
|
"""Set the given Temperature on the relevant Thermostat."""
|
334
|
-
|
354
|
+
try:
|
355
|
+
await self._smile_api.set_temperature(loc_id, items)
|
356
|
+
except ConnectionFailedError as exc:
|
357
|
+
raise ConnectionFailedError(f"Failed to set temperature: {str(exc)}") from exc
|
335
358
|
|
336
359
|
async def set_number(
|
337
360
|
self,
|
@@ -340,30 +363,58 @@ class Smile(SmileComm):
|
|
340
363
|
temperature: float,
|
341
364
|
) -> None:
|
342
365
|
"""Set the maximum boiler- or DHW-setpoint on the Central Heating boiler or the temperature-offset on a Thermostat."""
|
343
|
-
|
366
|
+
try:
|
367
|
+
await self._smile_api.set_number(dev_id, key, temperature)
|
368
|
+
except ConnectionFailedError as exc:
|
369
|
+
raise ConnectionFailedError(f"Failed to set number '{key}': {str(exc)}") from exc
|
344
370
|
|
345
371
|
async def set_temperature_offset(self, dev_id: str, offset: float) -> None:
|
346
372
|
"""Set the Temperature offset for thermostats that support this feature."""
|
347
|
-
|
373
|
+
try: # pragma no cover
|
374
|
+
await self._smile_api.set_offset(dev_id, offset) # pragma: no cover
|
375
|
+
except ConnectionFailedError as exc: # pragma no cover
|
376
|
+
raise ConnectionFailedError(f"Failed to set temperature offset: {str(exc)}") from exc # pragma no cover
|
348
377
|
|
349
378
|
async def set_switch_state(
|
350
379
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
351
380
|
) -> None:
|
352
381
|
"""Set the given State of the relevant Switch."""
|
353
|
-
|
382
|
+
try:
|
383
|
+
await self._smile_api.set_switch_state(appl_id, members, model, state)
|
384
|
+
except ConnectionFailedError as exc:
|
385
|
+
raise ConnectionFailedError(f"Failed to set switch state: {str(exc)}") from exc
|
354
386
|
|
355
387
|
async def set_gateway_mode(self, mode: str) -> None:
|
356
388
|
"""Set the gateway mode."""
|
357
|
-
|
389
|
+
try: # pragma no cover
|
390
|
+
await self._smile_api.set_gateway_mode(mode) # pragma: no cover
|
391
|
+
except ConnectionFailedError as exc: # pragma no cover
|
392
|
+
raise ConnectionFailedError(f"Failed to set gateway mode: {str(exc)}") from exc # pragma no cover
|
358
393
|
|
359
394
|
async def set_regulation_mode(self, mode: str) -> None:
|
360
395
|
"""Set the heating regulation mode."""
|
361
|
-
|
396
|
+
try: # pragma no cover
|
397
|
+
await self._smile_api.set_regulation_mode(mode) # pragma: no cover
|
398
|
+
except ConnectionFailedError as exc: # pragma no cover
|
399
|
+
raise ConnectionFailedError(f"Failed to set regulation mode: {str(exc)}") from exc # pragma no cover
|
362
400
|
|
363
401
|
async def set_dhw_mode(self, mode: str) -> None:
|
364
402
|
"""Set the domestic hot water heating regulation mode."""
|
365
|
-
|
403
|
+
try: # pragma no cover
|
404
|
+
await self._smile_api.set_dhw_mode(mode) # pragma: no cover
|
405
|
+
except ConnectionFailedError as exc: # pragma no cover
|
406
|
+
raise ConnectionFailedError(f"Failed to set dhw mode: {str(exc)}") from exc # pragma no cover
|
366
407
|
|
367
408
|
async def delete_notification(self) -> None:
|
368
409
|
"""Delete the active Plugwise Notification."""
|
369
|
-
|
410
|
+
try:
|
411
|
+
await self._smile_api.delete_notification()
|
412
|
+
except ConnectionFailedError as exc:
|
413
|
+
raise ConnectionFailedError(f"Failed to delete notification: {str(exc)}") from exc
|
414
|
+
|
415
|
+
async def reboot_gateway(self) -> None:
|
416
|
+
"""Reboot the Plugwise Gateway."""
|
417
|
+
try:
|
418
|
+
await self._smile_api.reboot_gateway()
|
419
|
+
except ConnectionFailedError as exc:
|
420
|
+
raise ConnectionFailedError(f"Failed to reboot gateway: {str(exc)}") from exc
|
@@ -17,6 +17,7 @@ DEGREE: Final = "°"
|
|
17
17
|
ELECTRIC_POTENTIAL_VOLT: Final = "V"
|
18
18
|
ENERGY_KILO_WATT_HOUR: Final = "kWh"
|
19
19
|
ENERGY_WATT_HOUR: Final = "Wh"
|
20
|
+
GATEWAY_REBOOT: Final = "/core/gateways;@reboot"
|
20
21
|
PERCENTAGE: Final = "%"
|
21
22
|
POWER_WATT: Final = "W"
|
22
23
|
PRESET_AWAY: Final = "away"
|
@@ -388,6 +389,7 @@ class GatewayData(TypedDict, total=False):
|
|
388
389
|
heater_id: str
|
389
390
|
item_count: int
|
390
391
|
notifications: dict[str, dict[str, str]]
|
392
|
+
reboot: bool
|
391
393
|
smile_name: str
|
392
394
|
|
393
395
|
|
@@ -9,6 +9,10 @@ class ConnectionFailedError(PlugwiseException):
|
|
9
9
|
"""Raised when unable to connect."""
|
10
10
|
|
11
11
|
|
12
|
+
class DataMissingError(PlugwiseException):
|
13
|
+
"""Raised when expected data is missing."""
|
14
|
+
|
15
|
+
|
12
16
|
class InvalidAuthentication(PlugwiseException):
|
13
17
|
"""Raised when unable to authenticate."""
|
14
18
|
|
@@ -29,14 +33,6 @@ class DeviceSetupError(PlugwiseException):
|
|
29
33
|
"""Raised when device is missing critical setup data."""
|
30
34
|
|
31
35
|
|
32
|
-
class DeviceTimeoutError(PlugwiseException):
|
33
|
-
"""Raised when device is not supported."""
|
34
|
-
|
35
|
-
|
36
|
-
class ErrorSendingCommandError(PlugwiseException):
|
37
|
-
"""Raised when device is not accepting the command."""
|
38
|
-
|
39
|
-
|
40
36
|
class ResponseError(PlugwiseException):
|
41
37
|
"""Raised when empty or error in response returned."""
|
42
38
|
|
@@ -115,22 +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
|
-
|
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
|
+
)
|
134
143
|
except (
|
135
144
|
ClientError
|
136
145
|
) as exc: # ClientError is an ancestor class of ServerTimeoutError
|
@@ -144,24 +153,41 @@ class SmileComm:
|
|
144
153
|
raise ConnectionFailedError from exc
|
145
154
|
return await self._request(command, retry - 1)
|
146
155
|
|
156
|
+
if resp.status == 504:
|
157
|
+
if retry < 1:
|
158
|
+
LOGGER.warning(
|
159
|
+
"Failed sending %s %s to Plugwise Smile, error: %s",
|
160
|
+
method,
|
161
|
+
command,
|
162
|
+
"504 Gateway Timeout",
|
163
|
+
)
|
164
|
+
raise ConnectionFailedError
|
165
|
+
return await self._request(command, retry - 1)
|
166
|
+
|
147
167
|
return await self._request_validate(resp, method)
|
148
168
|
|
149
169
|
async def _request_validate(self, resp: ClientResponse, method: str) -> etree:
|
150
170
|
"""Helper-function for _request(): validate the returned data."""
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
+
|
188
|
+
if not (result := await resp.text()) or (
|
189
|
+
"<error>" in result and "Not started" not in result
|
190
|
+
):
|
165
191
|
LOGGER.warning("Smile response empty or error in %s", result)
|
166
192
|
raise ResponseError
|
167
193
|
|
@@ -5,6 +5,7 @@ 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,
|
@@ -23,7 +24,7 @@ from plugwise.constants import (
|
|
23
24
|
PlugwiseData,
|
24
25
|
ThermoLoc,
|
25
26
|
)
|
26
|
-
from plugwise.exceptions import PlugwiseError
|
27
|
+
from plugwise.exceptions import ConnectionFailedError, PlugwiseError
|
27
28
|
from plugwise.helper import SmileComm
|
28
29
|
from plugwise.legacy.data import SmileLegacyData
|
29
30
|
|
@@ -149,6 +150,9 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
149
150
|
async def delete_notification(self) -> None:
|
150
151
|
"""Set-function placeholder for legacy devices."""
|
151
152
|
|
153
|
+
async def reboot_gateway(self) -> None:
|
154
|
+
"""Set-function placeholder for legacy devices."""
|
155
|
+
|
152
156
|
async def set_dhw_mode(self, mode: str) -> None:
|
153
157
|
"""Set-function placeholder for legacy devices."""
|
154
158
|
|
@@ -177,7 +181,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
177
181
|
rule = self._domain_objects.find(locator)
|
178
182
|
data = f'<rules><rule id="{rule.attrib["id"]}"><active>true</active></rule></rules>'
|
179
183
|
|
180
|
-
await self.
|
184
|
+
await self.call_request(RULES, method="put", data=data)
|
181
185
|
|
182
186
|
async def set_regulation_mode(self, mode: str) -> None:
|
183
187
|
"""Set-function placeholder for legacy devices."""
|
@@ -223,7 +227,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
223
227
|
f' id="{template_id}" /><active>{new_state}</active></rule></rules>'
|
224
228
|
)
|
225
229
|
|
226
|
-
await self.
|
230
|
+
await self.call_request(uri, method="put", data=data)
|
227
231
|
|
228
232
|
async def set_switch_state(
|
229
233
|
self, appl_id: str, members: list[str] | None, model: str, state: str
|
@@ -251,7 +255,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
251
255
|
if self._appliances.find(locator).text == "true":
|
252
256
|
raise PlugwiseError("Plugwise: the locked Relay was not switched.")
|
253
257
|
|
254
|
-
await self.
|
258
|
+
await self.call_request(uri, method="put", data=data)
|
255
259
|
|
256
260
|
async def _set_groupswitch_member_state(
|
257
261
|
self, members: list[str], state: str, switch: Munch
|
@@ -264,7 +268,7 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
264
268
|
uri = f"{APPLIANCES};id={member}/{switch.func_type}"
|
265
269
|
data = f"<{switch.func_type}><{switch.func}>{state}</{switch.func}></{switch.func_type}>"
|
266
270
|
|
267
|
-
await self.
|
271
|
+
await self.call_request(uri, method="put", data=data)
|
268
272
|
|
269
273
|
async def set_temperature(self, _: str, items: dict[str, float]) -> None:
|
270
274
|
"""Set the given Temperature on the relevant Thermostat."""
|
@@ -284,4 +288,13 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
|
|
284
288
|
f"{temperature}</setpoint></thermostat_functionality>"
|
285
289
|
)
|
286
290
|
|
287
|
-
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
|