Implement IOT Time Module Failover (#1583)
Some checks failed
CI / Perform Lint Checks (3.13) (push) Has been cancelled
CodeQL Checks / Analyze (python) (push) Has been cancelled
CI / Python 3.11 on macos-latest (push) Has been cancelled
CI / Python 3.12 on macos-latest (push) Has been cancelled
CI / Python 3.13 on macos-latest (push) Has been cancelled
CI / Python 3.11 on ubuntu-latest (push) Has been cancelled
CI / Python 3.12 on ubuntu-latest (push) Has been cancelled
CI / Python 3.13 on ubuntu-latest (push) Has been cancelled
CI / Python 3.11 on windows-latest (push) Has been cancelled
CI / Python 3.12 on windows-latest (push) Has been cancelled
CI / Python 3.13 on windows-latest (push) Has been cancelled
Stale / stale (push) Has been cancelled

Adds a failover in the IOT Time Module to handle issues where
the system default time zone files don't have the correct time zone
info.
This commit is contained in:
ZeliardM
2026-02-22 13:57:23 -05:00
committed by GitHub
parent eefbf9ec1c
commit 932f3e21e0
4 changed files with 560 additions and 20 deletions

View File

@@ -2,12 +2,19 @@
from __future__ import annotations
from datetime import UTC, datetime, tzinfo
import contextlib
from datetime import UTC, datetime, timedelta, tzinfo
from zoneinfo import ZoneInfoNotFoundError
from ...exceptions import KasaException
from ...interfaces import Time as TimeInterface
from ..iotmodule import IotModule, merge
from ..iottimezone import get_timezone, get_timezone_index
from ..iottimezone import (
_expected_dst_behavior_for_index,
_guess_timezone_by_offset,
get_timezone,
get_timezone_index,
)
class Time(IotModule, TimeInterface):
@@ -23,9 +30,46 @@ class Time(IotModule, TimeInterface):
return q
async def _post_update_hook(self) -> None:
"""Perform actions after a device update."""
"""Perform actions after a device update.
If the configured zone is not available on this host, compute the device's
current UTC offset and choose a best-match available zone, preferring DST-
observing candidates when the original index implies DST. As a last resort,
use a fixed-offset timezone.
"""
if res := self.data.get("get_timezone"):
self._timezone = await get_timezone(res.get("index"))
idx = res.get("index")
try:
self._timezone = await get_timezone(idx)
return
except ZoneInfoNotFoundError:
pass # fall through to offset-based match
gt = self.data.get("get_time")
if gt:
device_local = datetime(
gt["year"],
gt["month"],
gt["mday"],
gt["hour"],
gt["min"],
gt["sec"],
)
now_utc = datetime.now(UTC)
delta = device_local - now_utc.replace(tzinfo=None)
rounded = timedelta(seconds=60 * round(delta.total_seconds() / 60))
dst_expected = None
if res := self.data.get("get_timezone"):
idx = res.get("index")
with contextlib.suppress(KeyError):
dst_expected = _expected_dst_behavior_for_index(idx)
self._timezone = await _guess_timezone_by_offset(
rounded, when_utc=now_utc, dst_expected=dst_expected
)
else:
self._timezone = UTC
@property
def time(self) -> datetime: