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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.8.3

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

## 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.3"
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
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