Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Ongoing

- Remove storing the last active schedules, to be handled by the HA Integration via PR [#806](https://github.com/plugwise/python-plugwise/pull/806)

## v1.8.2

- Add support for Emma Pro wired, rename wireless Emma to Emma Pro via PR [#804](https://github.com/plugwise/python-plugwise/pull/804)
Expand Down
2 changes: 0 additions & 2 deletions plugwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ def __init__(
self._cooling_present = False
self._elga = False
self._is_thermostat = False
self._last_active: dict[str, str | None] = {}
self._loc_data: dict[str, ThermoLoc] = {}
self._on_off_device = False
self._opentherm_device = False
Expand Down Expand Up @@ -156,7 +155,6 @@ async def connect(self) -> Version:
self._cooling_present,
self._elga,
self._is_thermostat,
self._last_active,
self._loc_data,
self._on_off_device,
self._opentherm_device,
Expand Down
32 changes: 2 additions & 30 deletions plugwise/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from __future__ import annotations

import datetime as dt
from typing import cast

from plugwise.common import SmileCommon
Expand Down Expand Up @@ -50,9 +49,6 @@
skip_obsolete_measurements,
)

# Time related
from dateutil import tz
from dateutil.parser import parse
from defusedxml import ElementTree as etree
from munch import Munch
from packaging import version
Expand All @@ -78,7 +74,6 @@ def __init__(self) -> None:
self._endpoint: str
self._elga: bool
self._is_thermostat: bool
self._last_active: dict[str, str | None]
self._loc_data: dict[str, ThermoLoc]
self._schedule_old_states: dict[str, dict[str, str]]
self._gateway_id: str = NONE
Expand Down Expand Up @@ -871,7 +866,7 @@ def _rule_ids_by_name(self, name: str, loc_id: str) -> dict[str, dict[str, str]]
return schedule_ids

def _rule_ids_by_tag(self, tag: str, loc_id: str) -> dict[str, dict[str, str]]:
"""Helper-function for _presets(), _schedules() and _last_active_schedule().
"""Helper-function for _presets() and _schedules().

Obtain the rule_id from the given template_tag and provide the location_id, when present.
"""
Expand Down Expand Up @@ -906,11 +901,6 @@ def _schedules(self, location: str) -> tuple[list[str], str]:
available: list[str] = [NONE]
rule_ids: dict[str, dict[str, str]] = {}
selected = NONE
# Adam schedules, one schedule can be linked to various locations
# self._last_active contains the locations and the active schedule name per location, or None
if location not in self._last_active:
self._last_active[location] = None

tag = "zone_preset_based_on_time_and_presence_with_override"
if not (rule_ids := self._rule_ids_by_tag(tag, location)):
return available, selected
Expand All @@ -927,34 +917,16 @@ def _schedules(self, location: str) -> tuple[list[str], str]:
available.append(name)
if location == data["location"] and active:
selected = name
self._last_active[location] = selected
schedules.append(name)

if schedules:
available.remove(NONE)
available.append(OFF)
if selected == NONE:
selected = OFF
if self._last_active.get(location) is None:
self._last_active[location] = self._last_used_schedule(schedules)

return available, selected

def _last_used_schedule(self, schedules: list[str]) -> str:
"""Helper-function for _schedules().

Determine the last-used schedule based on the modified date.
"""
epoch = dt.datetime(1970, 1, 1, tzinfo=tz.tzutc())
schedules_dates: dict[str, float] = {}

for name in schedules:
result = self._domain_objects.find(f'./rule[name="{name}"]')
schedule_date = result.find("modified_date").text
schedule_time = parse(schedule_date)
schedules_dates[name] = (schedule_time - epoch).total_seconds()

return sorted(schedules_dates.items(), key=lambda kv: kv[1])[-1][0]
return available, selected

def _thermostat_uri(self, loc_id: str) -> str:
"""Helper-function for smile.py: set_temperature().
Expand Down
15 changes: 5 additions & 10 deletions plugwise/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ def __init__(
_cooling_present: bool,
_elga: bool,
_is_thermostat: bool,
_last_active: dict[str, str | None],
_loc_data: dict[str, ThermoLoc],
_on_off_device: bool,
_opentherm_device: bool,
Expand All @@ -81,7 +80,6 @@ def __init__(
self._cooling_present = _cooling_present
self._elga = _elga
self._is_thermostat = _is_thermostat
self._last_active = _last_active
self._loc_data = _loc_data
self._on_off_device = _on_off_device
self._opentherm_device = _opentherm_device
Expand Down Expand Up @@ -325,14 +323,12 @@ async def set_schedule_state(
if name == OFF:
new_state = STATE_OFF

# Handle no schedule-name / Off-schedule provided
# Handle no schedule-name / schedule-off requested: find the active schedule
if name is None or name == OFF:
if schedule_name := self._last_active[loc_id]:
name = schedule_name
else:
_, name = self._schedules(loc_id)
if name == OFF: # no active schedule found, nothing to do
return

assert isinstance(name, str)
schedule_rule = self._rule_ids_by_name(name, loc_id)
# Raise an error when the schedule name does not exist
if not schedule_rule or schedule_rule is None:
Expand All @@ -351,7 +347,7 @@ async def set_schedule_state(
template_id = self._domain_objects.find(locator).attrib["id"]
template = f'<template id="{template_id}" />'

contexts = self.determine_contexts(loc_id, name, new_state, schedule_rule_id)
contexts = self.determine_contexts(loc_id, new_state, schedule_rule_id)
data = (
"<rules>"
f"<rule id='{schedule_rule_id}'>"
Expand All @@ -366,7 +362,7 @@ async def set_schedule_state(
self._schedule_old_states[loc_id][name] = new_state

def determine_contexts(
self, loc_id: str, name: str, state: str, sched_id: str
self, loc_id: str, state: str, sched_id: str
) -> str:
"""Helper-function for set_schedule_state()."""
locator = f'.//*[@id="{sched_id}"]/contexts'
Expand All @@ -377,7 +373,6 @@ def determine_contexts(
subject = etree.fromstring(subject)

if state == STATE_OFF:
self._last_active[loc_id] = name
contexts.remove(subject)
if state == STATE_ON:
contexts.append(subject)
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 = "1.8.2"
version = "1.8.3a0"
license = "MIT"
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
readme = "README.md"
Expand Down
29 changes: 0 additions & 29 deletions tests/test_adam.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ async def test_connect_adam_plus_anna_new(self):

await self.device_test(api, "2025-10-12 00:00:01", testdata)
assert api.gateway_id == "da224107914542988a88561b4452b0f6"
assert api._last_active["f2bf9048bef64cc5b6d5110154e33c81"] == "Weekschema"
assert (
api._last_active["f871b8c4d63549319221e294e4f88074"] == "Weekschema"
) # Badkamer
assert self.entity_items == 216
assert self.entity_list == [
"da224107914542988a88561b4452b0f6",
Expand Down Expand Up @@ -216,11 +212,6 @@ async def test_connect_adam_zone_per_device(self):

await self.device_test(api, "2022-05-16 00:00:01", testdata)
assert api.gateway_id == "fe799307f1624099878210aa0b9f1475"
assert api._last_active["12493538af164a409c6a1c79e38afe1c"] == BADKAMER_SCHEMA
assert api._last_active["c50f167537524366a5af7aa3942feb1e"] == GF7_WOONKAMER
assert api._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
assert api._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
assert api._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
assert self.entity_items == 379

assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
Expand Down Expand Up @@ -294,11 +285,6 @@ async def test_connect_adam_multiple_devices_per_zone(self):
)

await self.device_test(api, "2022-05-16 00:00:01", testdata)
assert api._last_active["12493538af164a409c6a1c79e38afe1c"] == BADKAMER_SCHEMA
assert api._last_active["c50f167537524366a5af7aa3942feb1e"] == GF7_WOONKAMER
assert api._last_active["82fa13f017d240daa0d0ea1775420f24"] == CV_JESSIE
assert api._last_active["08963fec7c53423ca5680aa4cb502c63"] == BADKAMER_SCHEMA
assert api._last_active["446ac08dd04d4eff8ac57489757b7314"] == BADKAMER_SCHEMA
assert self.entity_items == 385

assert "af82e4ccf9c548528166d38e560662a4" in self.notifications
Expand Down Expand Up @@ -335,16 +321,6 @@ async def test_adam_heatpump_cooling(self):
server, api, client = await self.connect_wrapper()

await self.device_test(api, "2022-01-02 00:00:01", testdata)
assert api._last_active["b52908550469425b812c87f766fe5303"] == WERKDAG_SCHEMA
assert api._last_active["20e735858f8146cead98b873177a4f99"] == WERKDAG_SCHEMA
assert api._last_active["e39529c79ab54fda9bed26cfc0447546"] == WERKDAG_SCHEMA
assert api._last_active["9a27714b970547ee9a6bdadc2b815ad5"] == WERKDAG_SCHEMA
assert api._last_active["93ac3f7bf25342f58cbb77c4a99ac0b3"] == WERKDAG_SCHEMA
assert api._last_active["fa5fa6b34f6b40a0972988b20e888ed4"] == WERKDAG_SCHEMA
assert api._last_active["04b15f6e884448288f811d29fb7b1b30"] == WERKDAG_SCHEMA
assert api._last_active["a562019b0b1f47a4bde8ebe3dbe3e8a9"] == WERKDAG_SCHEMA
assert api._last_active["8cf650a4c10c44819e426bed406aec34"] == WERKDAG_SCHEMA
assert api._last_active["5cc21042f87f4b4c94ccb5537c47a53f"] == WERKDAG_SCHEMA
assert self.entity_items == 518
assert self.cooling_present
assert self._cooling_enabled
Expand Down Expand Up @@ -392,7 +368,6 @@ async def test_connect_adam_plus_anna(self):

await self.device_test(api, "2020-03-22 00:00:01", testdata)
assert api.gateway_id == "b128b4bbbd1f47e9bf4d756e8fb5ee94"
assert api._last_active["009490cc2f674ce6b576863fbb64f867"] == "Weekschema"
assert self.entity_items == 82
assert "6fb89e35caeb4b1cb275184895202d84" in self.notifications

Expand Down Expand Up @@ -433,10 +408,6 @@ async def test_adam_plus_jip(self):

await self.device_test(api, "2021-06-20 00:00:01", testdata)
assert api.gateway_id == "b5c2386c6f6342669e50fe49dd05b188"
assert api._last_active["d58fec52899f4f1c92e4f8fad6d8c48c"] is None
assert api._last_active["06aecb3d00354375924f50c47af36bd2"] is None
assert api._last_active["d27aede973b54be484f6842d1b2802ad"] is None
assert api._last_active["13228dab8ce04617af318a2888b3c548"] is None
assert self.entity_items == 261

# Negative test
Expand Down
22 changes: 4 additions & 18 deletions tests/test_anna.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ async def test_connect_anna_v4(self):

await self.device_test(api, "2020-04-05 00:00:01", testdata)
assert api.gateway_id == "0466eae8520144c78afb29628384edeb"
assert api._last_active["eb5309212bf5407bb143e5bfa3b18aee"] == "Standaard"
assert self.entity_items == 60
assert not self.notifications

Expand Down Expand Up @@ -102,7 +101,6 @@ async def test_connect_anna_v4_dhw(self):
)

await self.device_test(api, "2020-04-05 00:00:01", testdata)
assert api._last_active["eb5309212bf5407bb143e5bfa3b18aee"] == "Standaard"
assert self.entity_items == 60
assert not self.notifications

Expand Down Expand Up @@ -160,7 +158,6 @@ async def test_connect_anna_without_boiler_fw441(self):
)

await self.device_test(api, "2022-05-16 00:00:01", testdata)
assert api._last_active["c34c6864216446528e95d88985e714cc"] == "Normaal"
assert self.entity_items == 41
assert not self.notifications

Expand Down Expand Up @@ -189,7 +186,6 @@ async def test_connect_anna_heatpump_heating(self):

await self.device_test(api, "2020-04-12 00:00:01", testdata)
assert api.gateway_id == "015ae9ea3f964e668e490fa39da3870b"
assert api._last_active["c784ee9fdab44e1395b8dee7d7a497d5"] == "standaard"
assert self.entity_items == 69
assert not self.notifications
assert self.cooling_present
Expand Down Expand Up @@ -245,7 +241,6 @@ async def test_connect_anna_heatpump_cooling(self):
)

await self.device_test(api, "2020-04-19 00:00:01", testdata)
assert api._last_active["c784ee9fdab44e1395b8dee7d7a497d5"] == "standaard"
assert self.entity_items == 66
assert self.cooling_present
assert not self.notifications
Expand Down Expand Up @@ -318,7 +313,6 @@ async def test_connect_anna_elga_no_cooling(self):

await self.device_test(api, "2020-04-12 00:00:01", testdata)
assert api.gateway_id == "015ae9ea3f964e668e490fa39da3870b"
assert api._last_active["c784ee9fdab44e1395b8dee7d7a497d5"] == "standaard"
assert self.entity_items == 65
assert not self.notifications
assert not self.cooling_present
Expand All @@ -342,9 +336,6 @@ async def test_connect_anna_elga_2(self):
)

await self.device_test(api, "2022-03-13 00:00:01", testdata)
assert (
api._last_active["d3ce834534114348be628b61b26d9220"] == THERMOSTAT_SCHEDULE
)
assert self.entity_items == 61
assert api.gateway_id == "fb49af122f6e4b0f91267e1cf7666d6f"
assert self.cooling_present
Expand All @@ -364,13 +355,13 @@ async def test_connect_anna_elga_2_schedule_off(self):
assert api.smile.hostname == "smile000000"

await self.device_test(api, "2022-03-13 00:00:01", testdata)
assert (
api._last_active["d3ce834534114348be628b61b26d9220"] == THERMOSTAT_SCHEDULE
)
assert self.cooling_present
assert not self._cooling_enabled
assert self.entity_items == 65

result = await self.tinker_thermostat(
api, "d3ce834534114348be628b61b26d9220", good_schedules=["Thermostat schedule", "off"]
)
assert result
Comment thread
bouwew marked this conversation as resolved.
await api.close_connection()
await self.disconnect(server, client)

Expand All @@ -394,9 +385,6 @@ async def test_connect_anna_elga_2_cooling(self):
)

await self.device_test(api, "2022-03-10 00:00:01", testdata)
assert (
api._last_active["d3ce834534114348be628b61b26d9220"] == THERMOSTAT_SCHEDULE
)
assert self.entity_items == 65
assert not self.notifications

Expand Down Expand Up @@ -450,7 +438,6 @@ async def test_connect_anna_loria_heating_idle(self):
)

await self.device_test(api, "2022-05-16 00:00:01", testdata)
assert api._last_active["15da035090b847e7a21f93e08c015ebc"] == "Winter"
assert self.entity_items == 68
assert self.cooling_present
assert not self._cooling_enabled
Expand Down Expand Up @@ -516,7 +503,6 @@ async def test_connect_anna_loria_cooling_active(self):
)

await self.device_test(api, "2022-05-16 00:00:01", testdata)
assert api._last_active["15da035090b847e7a21f93e08c015ebc"] == "Winter"
assert self.entity_items == 68
assert self.cooling_present
assert self._cooling_enabled
Expand Down