Skip to content

Commit 253d278

Browse files
authored
Merge pull request #176 from plugwise/set-return
Set-functions: rework to raise instead of return
2 parents 417f92c + 6807bfd commit 253d278

File tree

6 files changed

+87
-103
lines changed

6 files changed

+87
-103
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
# v0.17.5: Smile: rework to raise instead of return
4+
- raise in error-cases, move LOGGER.debug messages into raise
5+
- clean up code
6+
7+
# v0.17.4 - Smile: improve typing hints, implement mypy testing
8+
39
# v0.17.3 - Smile Adam: add support for heater_electric type Plugs
410

511
# v0.17.2 - Smile Adam: more bugfixes, improvementds

plugwise/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Plugwise module."""
22

3-
__version__ = "0.17.3"
3+
__version__ = "0.17.5"
44

55
from plugwise.smile import Smile
66
from plugwise.stick import Stick

plugwise/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ class InvalidSetupError(PlugwiseException):
5959
"""Raised when adding an Anna while an Adam exists."""
6060

6161

62+
class PlugwiseError(PlugwiseException):
63+
"""Raise when a non-specific error happens."""
64+
65+
6266
class UnsupportedDeviceError(PlugwiseException):
6367
"""Raised when device is not supported."""
6468

plugwise/helper.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,18 +227,18 @@ async def _request_validate(self, resp: ClientResponse, method: str) -> etree:
227227
return
228228

229229
if resp.status == 401:
230-
raise InvalidAuthentication
230+
raise InvalidAuthentication(
231+
"Invalid login, please retry with the correct credentials."
232+
)
231233

232234
if not (result := await resp.text()) or "<error>" in result:
233-
LOGGER.error("Smile response empty or error in %s", result)
234-
raise ResponseError
235+
raise ResponseError(f"Smile response empty or error in {result}.")
235236

236237
try:
237238
# Encode to ensure utf8 parsing
238239
xml = etree.XML(escape_illegal_xml_characters(result).encode())
239240
except etree.ParseError:
240-
LOGGER.error("Smile returns invalid XML for %s", self._endpoint)
241-
raise InvalidXMLError
241+
raise InvalidXMLError(f"Smile returns invalid XML for {self._endpoint}.")
242242

243243
return xml
244244

@@ -271,8 +271,9 @@ async def _request(
271271
)
272272
except ServerTimeoutError:
273273
if retry < 1:
274-
LOGGER.error("Timed out sending %s command to Plugwise", command)
275-
raise DeviceTimeoutError
274+
raise DeviceTimeoutError(
275+
f"Timed out sending {command} command to Plugwise"
276+
)
276277
return await self._request(command, retry - 1)
277278

278279
return await self._request_validate(resp, method)

plugwise/smile.py

Lines changed: 48 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .exceptions import (
3535
ConnectionFailedError,
3636
InvalidSetupError,
37-
InvalidXMLError,
37+
PlugwiseError,
3838
UnsupportedDeviceError,
3939
)
4040
from .helper import SmileComm, SmileHelper, update_helper
@@ -275,21 +275,18 @@ async def connect(self) -> bool:
275275
dsmrmain = result.find("./module/protocols/dsmrmain")
276276
if "Plugwise" not in names:
277277
if dsmrmain is None: # pragma: no cover
278-
LOGGER.error(
279-
"Connected but expected text not returned, \
280-
we got %s. Please create an issue on \
281-
http://github.com/plugwise/python-plugwise",
282-
result,
278+
raise ConnectionFailedError(
279+
f"Connected but expected text not returned, \
280+
we got {result}. Please create an issue on \
281+
http://github.com/plugwise/python-plugwise"
283282
)
284-
raise ConnectionFailedError
285283

286284
# Check if Anna is connected to an Adam
287285
if "159.2" in models:
288-
LOGGER.error(
286+
raise InvalidSetupError(
289287
"Your Anna is connected to an Adam, make \
290-
sure to only add the Adam as integration.",
288+
sure to only add the Adam as integration."
291289
)
292-
raise InvalidSetupError
293290

294291
# Determine smile specifics
295292
await self._smile_detect(result, dsmrmain)
@@ -320,38 +317,31 @@ async def _smile_detect_legacy(self, result: etree, dsmrmain: etree) -> str:
320317
if result.find('./appliance[type="thermostat"]') is None:
321318
# It's a P1 legacy:
322319
if dsmrmain is not None:
323-
try:
324-
status = await self._request(STATUS)
325-
self.smile_fw_version = status.find("./system/version").text
326-
model = status.find("./system/product").text
327-
self.smile_hostname = status.find("./network/hostname").text
328-
self.smile_mac_address = status.find("./network/mac_address").text
329-
except InvalidXMLError: # pragma: no cover
330-
# Corner case check
331-
raise ConnectionFailedError
320+
status = await self._request(STATUS)
321+
self.smile_fw_version = status.find("./system/version").text
322+
model = status.find("./system/product").text
323+
self.smile_hostname = status.find("./network/hostname").text
324+
self.smile_mac_address = status.find("./network/mac_address").text
332325

333326
# Or a legacy Stretch:
334327
elif network is not None:
335-
try:
336-
system = await self._request(SYSTEM)
337-
self.smile_fw_version = system.find("./gateway/firmware").text
338-
model = system.find("./gateway/product").text
339-
self.smile_hostname = system.find("./gateway/hostname").text
340-
# If wlan0 contains data it's active, so eth0 should be checked last
341-
for network in ["wlan0", "eth0"]:
342-
locator = f"./{network}/mac"
343-
if (net_locator := system.find(locator)) is not None:
344-
self.smile_mac_address = net_locator.text
345-
except InvalidXMLError: # pragma: no cover
346-
# Corner case check
347-
raise ConnectionFailedError
328+
system = await self._request(SYSTEM)
329+
self.smile_fw_version = system.find("./gateway/firmware").text
330+
model = system.find("./gateway/product").text
331+
self.smile_hostname = system.find("./gateway/hostname").text
332+
# If wlan0 contains data it's active, so eth0 should be checked last
333+
for network in ["wlan0", "eth0"]:
334+
locator = f"./{network}/mac"
335+
if (net_locator := system.find(locator)) is not None:
336+
self.smile_mac_address = net_locator.text
337+
348338
else: # pragma: no cover
349339
# No cornercase, just end of the line
350-
LOGGER.error(
340+
raise ConnectionFailedError(
351341
"Connected but no gateway device information found, please create \
352342
an issue on http://github.com/plugwise/python-plugwise"
353343
)
354-
raise ConnectionFailedError
344+
355345
return model
356346

357347
async def _smile_detect(self, result: etree, dsmrmain: etree) -> None:
@@ -370,23 +360,20 @@ async def _smile_detect(self, result: etree, dsmrmain: etree) -> None:
370360

371361
if model is None or self.smile_fw_version is None: # pragma: no cover
372362
# Corner case check
373-
LOGGER.error(
363+
raise UnsupportedDeviceError(
374364
"Unable to find model or version information, please create \
375365
an issue on http://github.com/plugwise/python-plugwise"
376366
)
377-
raise UnsupportedDeviceError
378367

379368
ver = semver.VersionInfo.parse(self.smile_fw_version)
380369
target_smile = f"{model}_v{ver.major}"
381370
LOGGER.debug("Plugwise identified as %s", target_smile)
382371
if target_smile not in SMILES:
383-
LOGGER.error(
384-
'Your version Smile identified as "%s" seems\
372+
raise UnsupportedDeviceError(
373+
"Your version Smile identified as {target_smile} seems\
385374
unsupported by our plugin, please create an issue\
386-
on http://github.com/plugwise/python-plugwise',
387-
target_smile,
375+
on http://github.com/plugwise/python-plugwise"
388376
)
389-
raise UnsupportedDeviceError
390377

391378
self.smile_name = SMILES[target_smile]["friendly_name"]
392379
self.smile_type = SMILES[target_smile]["type"]
@@ -472,15 +459,15 @@ async def async_update(self) -> list[dict[str, Any]]:
472459

473460
return [self.gw_data, self.gw_devices]
474461

475-
async def _set_schedule_state_legacy(self, name: str, status: str) -> bool:
462+
async def _set_schedule_state_legacy(self, name: str, status: str) -> None:
476463
"""Helper-function for set_schedule_state()."""
477464
schedule_rule_id: str | None = None
478465
for rule in self._domain_objects.findall("rule"):
479466
if rule.find("name").text == name:
480467
schedule_rule_id = rule.attrib["id"]
481468

482469
if schedule_rule_id is None:
483-
return False
470+
raise PlugwiseError("No schedule available.")
484471

485472
state = "false"
486473
if status == "on":
@@ -497,18 +484,18 @@ async def _set_schedule_state_legacy(self, name: str, status: str) -> bool:
497484
)
498485

499486
await self._request(uri, method="put", data=data)
500-
return True
501487

502-
async def set_schedule_state(self, loc_id: str, name: str, state: str) -> bool:
488+
async def set_schedule_state(self, loc_id: str, name: str, state: str) -> None:
503489
"""Set the Schedule, with the given name, on the relevant Thermostat.
504490
Determined from - DOMAIN_OBJECTS.
505491
"""
506492
if self._smile_legacy:
507-
return await self._set_schedule_state_legacy(name, state)
493+
await self._set_schedule_state_legacy(name, state)
494+
return
508495

509496
schedule_rule = self._rule_ids_by_name(name, loc_id)
510497
if not schedule_rule or schedule_rule is None:
511-
return False
498+
raise PlugwiseError("No schedule with this name available.")
512499

513500
schedule_rule_id: str = next(iter(schedule_rule))
514501

@@ -543,31 +530,29 @@ async def set_schedule_state(self, loc_id: str, name: str, state: str) -> bool:
543530
)
544531
await self._request(uri, method="put", data=data)
545532

546-
return True
547-
548-
async def _set_preset_legacy(self, preset: str) -> bool:
533+
async def _set_preset_legacy(self, preset: str) -> None:
549534
"""Set the given Preset on the relevant Thermostat - from DOMAIN_OBJECTS."""
550535
locator = f'rule/directives/when/then[@icon="{preset}"].../.../...'
551536
if (rule := self._domain_objects.find(locator)) is None:
552-
return False
537+
raise PlugwiseError("Invalid preset.")
553538

554539
uri = RULES
555540
data = f'<rules><rule id="{rule.attrib["id"]}"><active>true</active></rule></rules>'
556541

557542
await self._request(uri, method="put", data=data)
558-
return True
559543

560-
async def set_preset(self, loc_id: str, preset: str) -> bool:
544+
async def set_preset(self, loc_id: str, preset: str) -> None:
561545
"""Set the given Preset on the relevant Thermostat - from LOCATIONS."""
562546
if self._smile_legacy:
563-
return await self._set_preset_legacy(preset)
547+
await self._set_preset_legacy(preset)
548+
return
564549

565550
current_location = self._locations.find(f'location[@id="{loc_id}"]')
566551
location_name = current_location.find("name").text
567552
location_type = current_location.find("type").text
568553

569554
if preset not in self._presets(loc_id):
570-
return False
555+
raise PlugwiseError("Invalid preset.")
571556

572557
uri = f"{LOCATIONS};id={loc_id}"
573558
data = (
@@ -577,9 +562,8 @@ async def set_preset(self, loc_id: str, preset: str) -> bool:
577562
)
578563

579564
await self._request(uri, method="put", data=data)
580-
return True
581565

582-
async def set_temperature(self, loc_id: str, temperature: str) -> bool:
566+
async def set_temperature(self, loc_id: str, temperature: str) -> None:
583567
"""Set the given Temperature on the relevant Thermostat."""
584568
uri = self._thermostat_uri(loc_id)
585569
data = (
@@ -588,9 +572,8 @@ async def set_temperature(self, loc_id: str, temperature: str) -> bool:
588572
)
589573

590574
await self._request(uri, method="put", data=data)
591-
return True
592575

593-
async def set_max_boiler_temperature(self, temperature: str) -> bool:
576+
async def set_max_boiler_temperature(self, temperature: str) -> None:
594577
"""Set the max. Boiler Temperature on the Central heating boiler."""
595578
locator = f'appliance[@id="{self._heater_id}"]/actuator_functionalities/thermostat_functionality'
596579
th_func = self._appliances.find(locator)
@@ -601,11 +584,10 @@ async def set_max_boiler_temperature(self, temperature: str) -> bool:
601584
data = f"<thermostat_functionality><setpoint>{temperature}</setpoint></thermostat_functionality>"
602585

603586
await self._request(uri, method="put", data=data)
604-
return True
605587

606588
async def _set_groupswitch_member_state(
607589
self, members: list[str], state: str, switch: Munch
608-
) -> bool:
590+
) -> None:
609591
"""Helper-function for set_switch_state() .
610592
Set the given State of the relevant Switch within a group of members.
611593
"""
@@ -619,11 +601,9 @@ async def _set_groupswitch_member_state(
619601

620602
await self._request(uri, method="put", data=data)
621603

622-
return True
623-
624604
async def set_switch_state(
625605
self, appl_id: str, members: list[str] | None, model: str, state: str
626-
) -> bool:
606+
) -> None:
627607
"""Set the given State of the relevant Switch."""
628608
switch = Munch()
629609
switch.actuator = "actuator_functionalities"
@@ -659,15 +639,14 @@ async def set_switch_state(
659639
lock_state: str = self._appliances.find(locator).text
660640
# Don't bother switching a relay when the corresponding lock-state is true
661641
if lock_state == "true":
662-
return False
642+
raise PlugwiseError("Cannot switch a locked Relay.")
663643

664644
await self._request(uri, method="put", data=data)
665-
return True
666645

667-
async def set_regulation_mode(self, mode: str) -> bool:
646+
async def set_regulation_mode(self, mode: str) -> None:
668647
"""Set the heating regulation mode."""
669648
if mode not in self._allowed_modes:
670-
return False
649+
raise PlugwiseError("Invalid regulation mode.")
671650

672651
uri = f"{APPLIANCES};type=gateway/regulation_mode_control"
673652
duration = ""
@@ -676,11 +655,9 @@ async def set_regulation_mode(self, mode: str) -> bool:
676655
data = f"<regulation_mode_control_functionality>{duration}<mode>{mode}</mode></regulation_mode_control_functionality>"
677656

678657
await self._request(uri, method="put", data=data)
679-
return True
680658

681-
async def delete_notification(self) -> bool:
659+
async def delete_notification(self) -> None:
682660
"""Delete the active Plugwise Notification."""
683661
uri = NOTIFICATIONS
684662

685663
await self._request(uri, method="delete")
686-
return True

0 commit comments

Comments
 (0)