plugwise 0.37.9__py3-none-any.whl → 0.38.0__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.
plugwise/__init__.py CHANGED
@@ -18,7 +18,14 @@ from plugwise.constants import (
18
18
  PlugwiseData,
19
19
  ThermoLoc,
20
20
  )
21
- from plugwise.exceptions import InvalidSetupError, ResponseError, UnsupportedDeviceError
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
- if self.smile_legacy:
146
- self._smile_api = SmileLegacyAPI(
147
- self._host,
148
- self._passwd,
149
- self._websession,
150
- self._is_thermostat,
151
- self._on_off_device,
152
- self._opentherm_device,
153
- self._stretch_v2,
154
- self._target_smile,
155
- self.loc_data,
156
- self.smile_fw_version,
157
- self.smile_hostname,
158
- self.smile_hw_version,
159
- self.smile_mac_address,
160
- self.smile_model,
161
- self.smile_name,
162
- self.smile_type,
163
- self.smile_zigbee_mac_address,
164
- self._user,
165
- self._port,
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
- """Perform a first fetch of all XML data, needed for initialization."""
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
- """Determine the devices present from the obtained XML-data."""
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: PlugwiseData = await self._smile_api.async_update()
302
- self.gateway_id = data.gateway["gateway_id"]
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
  ########################################################################################################
@@ -366,4 +376,14 @@ class Smile(SmileComm):
366
376
 
367
377
  async def delete_notification(self) -> None:
368
378
  """Delete the active Plugwise Notification."""
369
- await self._smile_api.delete_notification()
379
+ try:
380
+ await self._smile_api.delete_notification()
381
+ except ConnectionFailedError as exc:
382
+ raise PlugwiseError(f"Failed to delete notification: {str(exc)}") from exc
383
+
384
+ async def reboot_gateway(self) -> None:
385
+ """Reboot the Plugwise Gateway."""
386
+ try:
387
+ await self._smile_api.reboot_gateway()
388
+ except ConnectionFailedError as exc:
389
+ raise PlugwiseError(f"Failed to reboot gateway: {str(exc)}") from exc
plugwise/constants.py CHANGED
@@ -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
 
plugwise/data.py CHANGED
@@ -38,6 +38,7 @@ class SmileData(SmileHelper):
38
38
  "gateway_id": self.gateway_id,
39
39
  "item_count": self._count,
40
40
  "notifications": self._notifications,
41
+ "reboot": True,
41
42
  "smile_name": self.smile_name,
42
43
  }
43
44
  )
plugwise/exceptions.py CHANGED
@@ -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
 
plugwise/helper.py CHANGED
@@ -123,6 +123,14 @@ class SmileComm:
123
123
  resp = await self._websession.get(
124
124
  url, headers=use_headers, auth=self._auth
125
125
  )
126
+ if method == "post":
127
+ use_headers = {"Content-type": "text/xml"}
128
+ resp = await self._websession.post(
129
+ url,
130
+ headers=use_headers,
131
+ data=data,
132
+ auth=self._auth,
133
+ )
126
134
  if method == "put":
127
135
  use_headers = {"Content-type": "text/xml"}
128
136
  resp = await self._websession.put(
@@ -144,6 +152,17 @@ class SmileComm:
144
152
  raise ConnectionFailedError from exc
145
153
  return await self._request(command, retry - 1)
146
154
 
155
+ if resp.status == 504:
156
+ if retry < 1:
157
+ LOGGER.warning(
158
+ "Failed sending %s %s to Plugwise Smile, error: %s",
159
+ method,
160
+ command,
161
+ "504 Gateway Timeout",
162
+ )
163
+ raise ConnectionFailedError
164
+ return await self._request(command, retry - 1)
165
+
147
166
  return await self._request_validate(resp, method)
148
167
 
149
168
  async def _request_validate(self, resp: ClientResponse, method: str) -> etree:
@@ -152,8 +171,8 @@ class SmileComm:
152
171
  if resp.status == 202:
153
172
  return
154
173
 
155
- # Cornercase for stretch not responding with 202
156
- if method == "put" and resp.status == 200:
174
+ # Cornercase for server not responding with 202
175
+ if method in ("post", "put") and resp.status == 200:
157
176
  return
158
177
 
159
178
  if resp.status == 401:
@@ -161,7 +180,14 @@ class SmileComm:
161
180
  LOGGER.error("%s", msg)
162
181
  raise InvalidAuthentication
163
182
 
164
- if not (result := await resp.text()) or "<error>" in result:
183
+ if resp.status == 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
 
plugwise/legacy/smile.py CHANGED
@@ -149,6 +149,9 @@ class SmileLegacyAPI(SmileComm, SmileLegacyData):
149
149
  async def delete_notification(self) -> None:
150
150
  """Set-function placeholder for legacy devices."""
151
151
 
152
+ async def reboot_gateway(self) -> None:
153
+ """Set-function placeholder for legacy devices."""
154
+
152
155
  async def set_dhw_mode(self, mode: str) -> None:
153
156
  """Set-function placeholder for legacy devices."""
154
157
 
plugwise/smile.py CHANGED
@@ -5,6 +5,7 @@ Plugwise backend module for Home Assistant Core.
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
  ADAM,
@@ -14,6 +15,7 @@ from plugwise.constants import (
14
15
  DEFAULT_TIMEOUT,
15
16
  DEFAULT_USERNAME,
16
17
  DOMAIN_OBJECTS,
18
+ GATEWAY_REBOOT,
17
19
  LOCATIONS,
18
20
  MAX_SETPOINT,
19
21
  MIN_SETPOINT,
@@ -26,7 +28,7 @@ from plugwise.constants import (
26
28
  ThermoLoc,
27
29
  )
28
30
  from plugwise.data import SmileData
29
- from plugwise.exceptions import PlugwiseError
31
+ from plugwise.exceptions import ConnectionFailedError, DataMissingError, PlugwiseError
30
32
  from plugwise.helper import SmileComm
31
33
 
32
34
  import aiohttp
@@ -131,13 +133,15 @@ class SmileAPI(SmileComm, SmileData):
131
133
  # Perform a full update at day-change
132
134
  self.gw_data: GatewayData = {}
133
135
  self.gw_devices: dict[str, DeviceData] = {}
134
- await self.full_update_device()
135
- self.get_all_devices()
136
-
137
- if "heater_id" in self.gw_data:
138
- self._heater_id = self.gw_data["heater_id"]
139
- if "cooling_enabled" in self.gw_devices[self._heater_id]["binary_sensors"]:
140
- self._cooling_enabled = self.gw_devices[self._heater_id]["binary_sensors"]["cooling_enabled"]
136
+ try:
137
+ await self.full_update_device()
138
+ self.get_all_devices()
139
+ if "heater_id" in self.gw_data:
140
+ self._heater_id = self.gw_data["heater_id"]
141
+ if "cooling_enabled" in self.gw_devices[self._heater_id]["binary_sensors"]:
142
+ self._cooling_enabled = self.gw_devices[self._heater_id]["binary_sensors"]["cooling_enabled"]
143
+ except KeyError as err:
144
+ raise DataMissingError("No Plugwise data received") from err
141
145
 
142
146
  return PlugwiseData(self.gw_data, self.gw_devices)
143
147
 
@@ -145,9 +149,21 @@ class SmileAPI(SmileComm, SmileData):
145
149
  ### API Set and HA Service-related Functions ###
146
150
  ########################################################################################################
147
151
 
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
+
148
160
  async def delete_notification(self) -> None:
149
161
  """Delete the active Plugwise Notification."""
150
- await self._request(NOTIFICATIONS, method="delete")
162
+ await self.call_request(NOTIFICATIONS, method="delete")
163
+
164
+ async def reboot_gateway(self) -> None:
165
+ """Reboot the Gateway."""
166
+ await self.call_request(GATEWAY_REBOOT, method="post")
151
167
 
152
168
  async def set_number(
153
169
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: plugwise
3
- Version: 0.37.9
3
+ Version: 0.38.0
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
  [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/plugwise)
57
- [![PyPI version fury.io](https://badge.fury.io/py/plugwise.svg)](https://pypi.python.org/pypi/plugwise/)
57
+ [![CodeRabbit.ai is Awesome](https://img.shields.io/badge/AI-orange?label=CodeRabbit&color=orange&link=https%3A%2F%2Fcoderabbit.ai)](https://coderabbit.ai)
58
+ [![renovate maintained](https://img.shields.io/badge/maintained%20with-renovate-blue?logo=renovatebot)](https://github.com/plugwise/python-plugwise/issues/291)
58
59
 
60
+ [![PyPI version fury.io](https://badge.fury.io/py/plugwise.svg)](https://pypi.python.org/pypi/plugwise/)
59
61
  [![Latest release](https://github.com/plugwise/python-plugwise/workflows/Latest%20release/badge.svg)](https://github.com/plugwise/python-plugwise/actions)
60
62
  [![Newest commit](https://github.com/plugwise/python-plugwise/workflows/Latest%20commit/badge.svg)](https://github.com/plugwise/python-plugwise/actions)
61
63
  [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/plugwise/python-plugwise/main.svg)](https://results.pre-commit.ci/latest/github/plugwise/python-plugwise/main)
62
- [![renovate maintained](https://img.shields.io/badge/maintained%20with-renovate-blue?logo=renovatebot)](https://github.com/plugwise/python-plugwise/issues/291)
63
64
 
64
65
  [![CodeFactor](https://www.codefactor.io/repository/github/plugwise/python-plugwise/badge)](https://www.codefactor.io/repository/github/plugwise/python-plugwise)
65
66
  [![codecov](https://codecov.io/gh/plugwise/python-plugwise/branch/main/graph/badge.svg)](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 make sure 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
+ 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` by @bouwew (Bouwe) & @CoMPaTech (Tom)
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.
@@ -0,0 +1,17 @@
1
+ plugwise/__init__.py,sha256=yJu-PvXj2gov6G85fCptfNAbMkAb_tMfiv8Uc9PgnQ8,14936
2
+ plugwise/common.py,sha256=P4sUYzgVcFsIR2DmQxeVeOiZvFZWpZXgwHA3XRc1Sx0,12538
3
+ plugwise/constants.py,sha256=aP2ifDFIRuzYzuhJk1cOdqN84yR135eqCzAATrhxMo4,16646
4
+ plugwise/data.py,sha256=HA3OoLrTad4ytns6_rfygwu8eGfopHJBNADGs-hvaQk,9054
5
+ plugwise/exceptions.py,sha256=Ce-tO9uNsMB-8FP6VAxBvsHNJ-NIM9F0onUZOdZI4Ys,1110
6
+ plugwise/helper.py,sha256=ctx5VbM9xQpNzHfL03WDP3H0yhhpC3MPI08LnhVUPxQ,43714
7
+ plugwise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ plugwise/smile.py,sha256=Dldh70NxbOrOPY6sIV79lb4YADsUOVR1vXr8m98aWd4,18520
9
+ plugwise/util.py,sha256=9ld46KJHWFze2eUrVSgUYn0g3zNerlpboM0iUa0H3ak,7830
10
+ plugwise/legacy/data.py,sha256=DsHR9xgiFDg_Vh_6ZpOskw8ZhNQ3CmwjstI3yiH6MEk,3048
11
+ plugwise/legacy/helper.py,sha256=6-tYQMEXepE5rec-hn6lt2EeknADI3J8UFuBSLgu8dk,17878
12
+ plugwise/legacy/smile.py,sha256=O8LzM1yCfdu2tiebUcm73bd7f39PIjGbwMxgDeZ_FkE,10864
13
+ plugwise-0.38.0.dist-info/LICENSE,sha256=mL22BjmXtg_wnoDnnaqps5_Bg_VGj_yHueX5lsKwbCc,1144
14
+ plugwise-0.38.0.dist-info/METADATA,sha256=5VGoiUgUS66yHXqvzJNupesOviaRbtyxS1z3OUMcnCs,9098
15
+ plugwise-0.38.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
+ plugwise-0.38.0.dist-info/top_level.txt,sha256=MYOmktMFf8ZmX6_OE1y9MoCZFfY-L8DA0F2tA2IvE4s,9
17
+ plugwise-0.38.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- plugwise/__init__.py,sha256=mI2mFZWucme1PJbsZssV7L7957nQot_LVS-ZeIcE-80,14427
2
- plugwise/common.py,sha256=P4sUYzgVcFsIR2DmQxeVeOiZvFZWpZXgwHA3XRc1Sx0,12538
3
- plugwise/constants.py,sha256=dvafW3u7LBbWUN05D1Tss7s4jxEDJiZDqnh4-q9isUM,16580
4
- plugwise/data.py,sha256=9sH2FkwGh5Uvbf42LmZPWutY9Qa-j9qmFZBobwJkLQo,9022
5
- plugwise/exceptions.py,sha256=rymGtWnXosdFkzSehc_41HVl2jXJEg_9Hq9APDEUwrk,1223
6
- plugwise/helper.py,sha256=nF1xJEw-frvuGMAydFAJrz_tN24Q3PcBnYNIwPeueZg,42817
7
- plugwise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- plugwise/smile.py,sha256=tzE6yxWd8oOO3pK0g2bTw8WcuVQ9tM7tG3p0z7-LqWU,17815
9
- plugwise/util.py,sha256=9ld46KJHWFze2eUrVSgUYn0g3zNerlpboM0iUa0H3ak,7830
10
- plugwise/legacy/data.py,sha256=DsHR9xgiFDg_Vh_6ZpOskw8ZhNQ3CmwjstI3yiH6MEk,3048
11
- plugwise/legacy/helper.py,sha256=6-tYQMEXepE5rec-hn6lt2EeknADI3J8UFuBSLgu8dk,17878
12
- plugwise/legacy/smile.py,sha256=kU6eOh0c4Ao_S0yOJeggDuicRR7r3gScjYCEGJTMowA,10760
13
- plugwise-0.37.9.dist-info/LICENSE,sha256=mL22BjmXtg_wnoDnnaqps5_Bg_VGj_yHueX5lsKwbCc,1144
14
- plugwise-0.37.9.dist-info/METADATA,sha256=imJmtku4hd0Dnxjf7QKpc9BOUPNCNTfuQhjwVhj6FZc,8939
15
- plugwise-0.37.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
16
- plugwise-0.37.9.dist-info/top_level.txt,sha256=MYOmktMFf8ZmX6_OE1y9MoCZFfY-L8DA0F2tA2IvE4s,9
17
- plugwise-0.37.9.dist-info/RECORD,,