Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v0.37.8

- Create a set_select() function, combining the set_dhw_mode(), set_gateway_mode(), set_regulation_mode() and set_schedule_state() functions.

## v0.37.7

- Don't output schedule-related data when no valid schedule(s) found.
Expand All @@ -17,6 +21,8 @@

## v0.37.4 - not released

- Create a set_number() function, combining the set_number_setpoint() and set_temperature_offset() functions.

## v0.37.3

- Fix for [plugwise-beta #620](https://github.com/plugwise/plugwise-beta/issues/620).
Expand Down
16 changes: 13 additions & 3 deletions plugwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,16 @@ async def async_update(self) -> PlugwiseData:
### API Set and HA Service-related Functions ###
########################################################################################################

async def set_select(
self,
key: str,
loc_id: str,
option: str,
name: str | None = None,
) -> None:
"""Set the selected option for the applicable Select."""
await self._smile_api.set_select(key, loc_id, option, name)

async def set_schedule_state(
self,
loc_id: str,
Expand Down Expand Up @@ -344,15 +354,15 @@ async def set_switch_state(

async def set_gateway_mode(self, mode: str) -> None:
"""Set the gateway mode."""
await self._smile_api.set_gateway_mode(mode)
await self._smile_api.set_gateway_mode(mode) # pragma: no cover

async def set_regulation_mode(self, mode: str) -> None:
"""Set the heating regulation mode."""
await self._smile_api.set_regulation_mode(mode)
await self._smile_api.set_regulation_mode(mode) # pragma: no cover

async def set_dhw_mode(self, mode: str) -> None:
"""Set the domestic hot water heating regulation mode."""
await self._smile_api.set_dhw_mode(mode)
await self._smile_api.set_dhw_mode(mode) # pragma: no cover

async def delete_notification(self) -> None:
"""Delete the active Plugwise Notification."""
Expand Down
6 changes: 6 additions & 0 deletions plugwise/legacy/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ async def set_preset(self, _: str, preset: str) -> None:
async def set_regulation_mode(self, mode: str) -> None:
"""Set-function placeholder for legacy devices."""

async def set_select(self, key: str, loc_id: str, option: str, name: str | None) -> None:
"""Set the thermostat schedule option."""
# schedule state corresponds to select option
# schedule name corresponds to select name
await self.set_schedule_state("dummy", option, name)

async def set_schedule_state(self, _: str, state: str, name: str | None) -> None:
"""Activate/deactivate the Schedule.

Expand Down
82 changes: 48 additions & 34 deletions plugwise/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,39 +149,6 @@ async def delete_notification(self) -> None:
"""Delete the active Plugwise Notification."""
await self._request(NOTIFICATIONS, method="delete")

async def set_dhw_mode(self, mode: str) -> None:
"""Set the domestic hot water heating regulation mode."""
if mode not in self._dhw_allowed_modes:
raise PlugwiseError("Plugwise: invalid dhw mode.")

uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control"
data = f"<domestic_hot_water_mode_control_functionality><mode>{mode}</mode></domestic_hot_water_mode_control_functionality>"

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

async def set_gateway_mode(self, mode: str) -> None:
"""Set the gateway mode."""
if mode not in self._gw_allowed_modes:
raise PlugwiseError("Plugwise: invalid gateway mode.")

end_time = "2037-04-21T08:00:53.000Z"
valid = ""
if mode == "away":
time_1 = self._domain_objects.find("./gateway/time").text
away_time = dt.datetime.fromisoformat(time_1).astimezone(dt.UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
valid = (
f"<valid_from>{away_time}</valid_from><valid_to>{end_time}</valid_to>"
)
if mode == "vacation":
time_2 = str(dt.date.today() - dt.timedelta(1))
vacation_time = time_2 + "T23:00:00.000Z"
valid = f"<valid_from>{vacation_time}</valid_from><valid_to>{end_time}</valid_to>"

uri = f"{APPLIANCES};id={self.gateway_id}/gateway_mode_control"
data = f"<gateway_mode_control_functionality><mode>{mode}</mode>{valid}</gateway_mode_control_functionality>"

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

async def set_number(
self,
dev_id: str,
Expand Down Expand Up @@ -241,6 +208,53 @@ async def set_preset(self, loc_id: str, preset: str) -> None:

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

async def set_select(self, key: str, loc_id: str, option: str, name: str | None) -> None:
"""Set a dhw/gateway/regulation mode or the thermostat schedule option."""
match key:
case "select_dhw_mode":
await self.set_dhw_mode(option)
case "select_gateway_mode":
await self.set_gateway_mode(option)
case "select_regulation_mode":
await self.set_regulation_mode(option)
case "select_schedule":
# schedule state corresponds to select option
# schedule name corresponds to select name
await self.set_schedule_state(loc_id, option, name)

async def set_dhw_mode(self, mode: str) -> None:
"""Set the domestic hot water heating regulation mode."""
if mode not in self._dhw_allowed_modes:
raise PlugwiseError("Plugwise: invalid dhw mode.")

uri = f"{APPLIANCES};type=heater_central/domestic_hot_water_mode_control"
data = f"<domestic_hot_water_mode_control_functionality><mode>{mode}</mode></domestic_hot_water_mode_control_functionality>"

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

async def set_gateway_mode(self, mode: str) -> None:
"""Set the gateway mode."""
if mode not in self._gw_allowed_modes:
raise PlugwiseError("Plugwise: invalid gateway mode.")

end_time = "2037-04-21T08:00:53.000Z"
valid = ""
if mode == "away":
time_1 = self._domain_objects.find("./gateway/time").text
away_time = dt.datetime.fromisoformat(time_1).astimezone(dt.UTC).isoformat(timespec="milliseconds").replace("+00:00", "Z")
valid = (
f"<valid_from>{away_time}</valid_from><valid_to>{end_time}</valid_to>"
)
if mode == "vacation":
time_2 = str(dt.date.today() - dt.timedelta(1))
vacation_time = time_2 + "T23:00:00.000Z"
valid = f"<valid_from>{vacation_time}</valid_from><valid_to>{end_time}</valid_to>"

uri = f"{APPLIANCES};id={self.gateway_id}/gateway_mode_control"
data = f"<gateway_mode_control_functionality><mode>{mode}</mode>{valid}</gateway_mode_control_functionality>"

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

async def set_regulation_mode(self, mode: str) -> None:
"""Set the heating regulation mode."""
if mode not in self._reg_allowed_modes:
Expand All @@ -258,7 +272,7 @@ async def set_schedule_state(
self,
loc_id: str,
new_state: str,
name: str | None = None,
name: str | None,
) -> None:
"""Activate/deactivate the Schedule, with the given name, on the relevant Thermostat.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "plugwise"
version = "0.37.7"
version = "0.37.8"
license = {file = "LICENSE"}
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
readme = "README.md"
Expand Down
10 changes: 5 additions & 5 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ async def tinker_thermostat_schedule(
new_schedule = new_schedule[1:]
_LOGGER.info("- Adjusting schedule to %s", f"{new_schedule}{warning}")
try:
await smile.set_schedule_state(loc_id, state, new_schedule)
await smile.set_select("select_schedule", loc_id, state, new_schedule)
tinker_schedule_passed = True
_LOGGER.info(" + working as intended")
except pw_exceptions.PlugwiseError:
Expand Down Expand Up @@ -763,7 +763,7 @@ async def tinker_legacy_thermostat_schedule(self, smile, unhappy=False):
for state in states:
_LOGGER.info("- Adjusting schedule to state %s", state)
try:
await smile.set_schedule_state(None, state)
await smile.set_select("select_schedule", "dummy", state)
tinker_schedule_passed = True
_LOGGER.info(" + working as intended")
except pw_exceptions.PlugwiseError:
Expand Down Expand Up @@ -847,7 +847,7 @@ async def tinker_dhw_mode(smile):
mode = mode[1:]
_LOGGER.info("%s", f"- Adjusting dhw mode to {mode}{warning}")
try:
await smile.set_dhw_mode(mode)
await smile.set_select("select_dhw_mode", "dummy", mode)
_LOGGER.info(" + tinker_dhw_mode worked as intended")
except pw_exceptions.PlugwiseError:
_LOGGER.info(" + tinker_dhw_mode found invalid mode, as expected")
Expand All @@ -862,7 +862,7 @@ async def tinker_regulation_mode(smile):
mode = mode[1:]
_LOGGER.info("%s", f"- Adjusting regulation mode to {mode}{warning}")
try:
await smile.set_regulation_mode(mode)
await smile.set_select("select_regulation_mode", "dummy", mode)
_LOGGER.info(" + tinker_regulation_mode worked as intended")
except pw_exceptions.PlugwiseError:
_LOGGER.info(
Expand Down Expand Up @@ -904,7 +904,7 @@ async def tinker_gateway_mode(smile):
mode = mode[1:]
_LOGGER.info("%s", f"- Adjusting gateway mode to {mode}{warning}")
try:
await smile.set_gateway_mode(mode)
await smile.set_select("select_gateway_mode", "dummy", mode)
_LOGGER.info(" + worked as intended")
except pw_exceptions.PlugwiseError:
_LOGGER.info(" + found invalid mode, as expected")
Expand Down