Skip to content

Commit 71855e4

Browse files
authored
Merge pull request #450 from plugwise/schedule_off
Add Off-option in the available-schedules list
2 parents 202263d + e21bf5e commit 71855e4

File tree

6 files changed

+158
-60
lines changed

6 files changed

+158
-60
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Changelog
22

3-
## Ongoing
3+
## v0.35.0
44

5+
- Feature: add "Off" as option in available_schedules, selecting this option will disable the active schedule for a thermostat.
6+
- Fix not being able to turn off a schedule.
57
- Update fixture to create a testcase for HVACAction.PREHEATING
68

79
## v0.34.5

plugwise/__init__.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
MAX_SETPOINT,
2828
MIN_SETPOINT,
2929
MODULES,
30+
NONE,
3031
NOTIFICATIONS,
32+
OFF,
3133
REQUIRE_APPLIANCES,
3234
RULES,
3335
SMILES,
@@ -201,13 +203,6 @@ def _device_data_adam(
201203

202204
return device_data
203205

204-
def check_reg_mode(self, mode: str) -> bool:
205-
"""Helper-function for device_data_climate()."""
206-
gateway = self.gw_devices[self.gateway_id]
207-
return (
208-
"regulation_modes" in gateway and gateway["select_regulation_mode"] == mode
209-
)
210-
211206
def _device_data_climate(
212207
self, device: DeviceData, device_data: DeviceData
213208
) -> DeviceData:
@@ -239,7 +234,7 @@ def _device_data_climate(
239234
# Operation modes: auto, heat, heat_cool, cool and off
240235
device_data["mode"] = "auto"
241236
self._count += 1
242-
if sel_schedule == "None":
237+
if sel_schedule == NONE:
243238
device_data["mode"] = "heat"
244239
if self._cooling_present:
245240
device_data["mode"] = (
@@ -249,17 +244,46 @@ def _device_data_climate(
249244
if self.check_reg_mode("off"):
250245
device_data["mode"] = "off"
251246

252-
if "None" not in avail_schedules:
253-
loc_schedule_states = {}
254-
for schedule in avail_schedules:
255-
loc_schedule_states[schedule] = (
256-
"off" if device_data["mode"] == "auto" else "on"
257-
)
258-
259-
self._schedule_old_states[loc_id] = loc_schedule_states
247+
if NONE in avail_schedules:
248+
return device_data
260249

250+
device_data = self._get_schedule_states_with_off(
251+
loc_id, avail_schedules, sel_schedule, device_data
252+
)
261253
return device_data
262254

255+
def check_reg_mode(self, mode: str) -> bool:
256+
"""Helper-function for device_data_climate()."""
257+
gateway = self.gw_devices[self.gateway_id]
258+
return (
259+
"regulation_modes" in gateway and gateway["select_regulation_mode"] == mode
260+
)
261+
262+
def _get_schedule_states_with_off(
263+
self, location: str, schedules: list[str], selected: str, data: DeviceData
264+
) -> DeviceData:
265+
"""Collect schedules with states for each thermostat.
266+
267+
Also, replace NONE by OFF when none of the schedules are active,
268+
only for non-legacy thermostats.
269+
"""
270+
loc_schedule_states: dict[str, str] = {}
271+
for schedule in schedules:
272+
loc_schedule_states[schedule] = "off"
273+
if schedule == selected and data["mode"] == "auto":
274+
loc_schedule_states[schedule] = "on"
275+
self._schedule_old_states[location] = loc_schedule_states
276+
277+
all_off = True
278+
if not self._smile_legacy:
279+
for state in self._schedule_old_states[location].values():
280+
if state == "on":
281+
all_off = False
282+
if all_off:
283+
data["select_schedule"] = OFF
284+
285+
return data
286+
263287
def _check_availability(
264288
self, device: DeviceData, device_data: DeviceData
265289
) -> DeviceData:
@@ -647,7 +671,13 @@ async def set_schedule_state(
647671
# Input checking
648672
if new_state not in ["on", "off"]:
649673
raise PlugwiseError("Plugwise: invalid schedule state.")
650-
if name is None:
674+
675+
# Translate selection of Off-schedule-option to disabling the active schedule
676+
if name == OFF:
677+
new_state = "off"
678+
679+
# Handle no schedule-name / Off-schedule provided
680+
if name is None or name == OFF:
651681
if schedule_name := self._last_active[loc_id]:
652682
name = schedule_name
653683
else:
@@ -668,7 +698,6 @@ async def set_schedule_state(
668698
return
669699

670700
schedule_rule_id: str = next(iter(schedule_rule))
671-
672701
template = (
673702
'<template tag="zone_preset_based_on_time_and_presence_with_override" />'
674703
)
@@ -683,6 +712,7 @@ async def set_schedule_state(
683712
f'<rules><rule id="{schedule_rule_id}"><name><![CDATA[{name}]]></name>'
684713
f"{template}{contexts}</rule></rules>"
685714
)
715+
686716
await self._request(uri, method="put", data=data)
687717
self._schedule_old_states[loc_id][name] = new_state
688718

plugwise/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
MAX_SETPOINT: Final[float] = 30.0
8181
MIN_SETPOINT: Final[float] = 4.0
8282
NONE: Final = "None"
83+
OFF: Final = "Off"
8384

8485
# XML data paths
8586
APPLIANCES: Final = "/core/appliances"

plugwise/helper.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
LOGGER,
4343
NONE,
4444
OBSOLETE_MEASUREMENTS,
45+
OFF,
4546
P1_LEGACY_MEASUREMENTS,
4647
P1_MEASUREMENTS,
4748
POWER_WATT,
@@ -1446,6 +1447,7 @@ def _schedules(self, location: str) -> tuple[list[str], str]:
14461447

14471448
if schedules:
14481449
available.remove(NONE)
1450+
available.append(OFF)
14491451
if self._last_active.get(location) is None:
14501452
self._last_active[location] = self._last_used_schedule(schedules)
14511453

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plugwise"
7-
version = "0.34.5"
7+
version = "0.35.0"
88
license = {file = "LICENSE"}
99
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
1010
readme = "README.md"

0 commit comments

Comments
 (0)