Skip to content

Commit 53881c8

Browse files
authored
Replace backoff with tenacity and handle always-off zones
1 parent 219a586 commit 53881c8

4 files changed

Lines changed: 56 additions & 21 deletions

File tree

poetry.lock

Lines changed: 18 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ package-mode = false
55
python = ">=3.13.2,<4.0"
66
evohome-async = ">=1.0,<2"
77
aiohttp = "^3.11"
8-
backoff = "^2.2"
8+
tenacity = "^9.1"
99

1010
[tool.poetry.group.dev.dependencies]
1111
pytest = "^9.0"

src/evohome_helper/evohome.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import asyncio
2-
import backoff
32
import logging
43
import settings
54

65
from datetime import datetime, timedelta
76
from evohomeasync2 import EvohomeClientOld as EvohomeClient, ControlSystem, Location, Zone
87
from evohomeasync2.schemas import SystemMode, ZoneMode
98
from evohome_helper import weather
9+
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
1010
from typing import Generator
1111

1212
logger = logging.getLogger(__name__)
1313
_evohome_client = None
14-
_retry = backoff.on_exception(wait_gen=backoff.expo, exception=Exception, max_tries=6)
14+
_retry = retry(
15+
retry=retry_if_exception_type(Exception),
16+
wait=wait_exponential(),
17+
stop=stop_after_attempt(6),
18+
reraise=True,
19+
)
1520

1621
_AWAY_MODE_MAP = {
1722
"auto": SystemMode.AUTO,
@@ -102,13 +107,17 @@ def _get_zone_switch_points(zone: Zone, now: datetime) -> list[tuple[datetime, f
102107
return result
103108

104109

105-
def _get_last_heating_switchpoint(zone: Zone, now: datetime) -> tuple[datetime, float]:
106-
last_heating_datetime = now - timedelta(weeks=52)
107-
last_heating_temperature = -99.0
110+
def _get_last_heating_switchpoint(zone: Zone, now: datetime) -> tuple[datetime, float] | None:
111+
last_heating_datetime = None
112+
last_heating_temperature = None
108113
for switchpoint_datetime, switchpoint_temperature in _get_zone_switch_points(zone, now):
109114
if not _is_considered_off(switchpoint_temperature):
110115
last_heating_datetime = switchpoint_datetime
111116
last_heating_temperature = switchpoint_temperature
117+
118+
if last_heating_datetime is None or last_heating_temperature is None:
119+
return None
120+
112121
return last_heating_datetime, last_heating_temperature
113122

114123

@@ -124,7 +133,12 @@ def is_in_schedule_grace_period(location: Location) -> bool:
124133

125134
zones = get_zones(location)
126135
for zone in zones:
127-
switch_point_start, switch_point_temperature = _get_last_heating_switchpoint(zone, now)
136+
switch_point = _get_last_heating_switchpoint(zone, now)
137+
if switch_point is None:
138+
logger.debug("no scheduled heating switch point found for %s", zone.name)
139+
continue
140+
141+
switch_point_start, switch_point_temperature = switch_point
128142
logger.debug(
129143
"last scheduled switch point for %s was at: %s (%s degrees celsius)",
130144
zone.name,

tests/test_evohome.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ def test_current_zone_switch_point_ignores_off_period(evohome_factory):
162162
assert sp_start == datetime(2024, 4, 7, 11, 55, 0)
163163

164164

165+
def test_get_last_heating_switchpoint_returns_none_when_zone_always_off(monkeypatch, evohome_factory):
166+
state = evohome_factory.complete_state(schedule=evohome_factory.uniform_schedule(15, "11:55:00"))
167+
monkeypatch.setattr("settings.EVOHOME_OFF_TEMP_THRESHOLD", 15)
168+
169+
now = datetime(2024, 4, 7, 12, 10, 0)
170+
assert evohome._get_last_heating_switchpoint(state.zone, now) is None
171+
172+
165173
def test_is_in_schedule_grace_period_triggered_after_off_within_grace(monkeypatch, evohome_factory):
166174
"""Grace period uses last heating start even if current period is off."""
167175
sp_heat = evohome_factory.switchpoint("11:55:00", 20)
@@ -176,6 +184,15 @@ def test_is_in_schedule_grace_period_triggered_after_off_within_grace(monkeypatc
176184
assert evohome.is_in_schedule_grace_period(state.location) is True
177185

178186

187+
def test_is_in_schedule_grace_period_false_when_zone_always_off(monkeypatch, evohome_factory):
188+
state = evohome_factory.complete_state(schedule=evohome_factory.uniform_schedule(15, "11:55:00"))
189+
monkeypatch.setattr("settings.PRESENCE_HEATING_SCHEDULE_GRACE_TIME", 900)
190+
monkeypatch.setattr("settings.EVOHOME_OFF_TEMP_THRESHOLD", 15)
191+
192+
with freeze_time("2024-04-07 12:00:00"):
193+
assert evohome.is_in_schedule_grace_period(state.location) is False
194+
195+
179196
def test_get_zones_filters_faulty_zones(evohome_factory):
180197
state = evohome_factory.complete_state(with_fault=False)
181198
faulty = evohome_factory.zone(name="bad", active_faults=["fault"])

0 commit comments

Comments
 (0)