FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fall back to UTC for iot time when the device clock is not set (#1768) · IDavinciCode/python-kasa@9030179 · GitHub

Repository navigation

Commit 9030179

Browse files
andauthored
Fall back to UTC for iot time when the device clock is not set (python-kasa#1768)
A new or factory-reset iot device can report a clock far in the past, e.g. year 2000, which causes a failure in timezone autodetection logic as the difference can be in years. This causes `update()` to fail, so every cli command fails, including `wifi scan` and `wifi join`, which are the commands needed to provision the device. To fix this, this changes the logic fall back to UTC if the device clock is not set, detected by checking if the timedelta is too large. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b2a15af commit 9030179

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

‎kasa/iot/modules/time.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ async def _post_update_hook(self) -> None:
3535
If the configured zone is not available on this host, compute the device's
3636
current UTC offset and choose a best-match available zone, preferring DST-
3737
observing candidates when the original index implies DST. As a last resort,
38-
use a fixed-offset timezone.
38+
use a fixed-offset timezone. If the device clock is not set, use UTC.
3939
"""
4040
if res := self.data.get("get_timezone"):
4141
idx = res.get("index")
@@ -58,6 +58,11 @@ async def _post_update_hook(self) -> None:
5858
now_utc = datetime.now(UTC)
5959
delta = device_local - now_utc.replace(tzinfo=None)
6060
rounded = timedelta(seconds=60 * round(delta.total_seconds() / 60))
61+
# UTC offsets range from -12h to +14h, anything beyond that means the
62+
# device clock is not set (e.g. before provisioning).
63+
if abs(rounded) > timedelta(hours=14):
64+
self._timezone = UTC
65+
return
6166

6267
dst_expected = None
6368
if res := self.data.get("get_timezone"):

‎tests/test_common_modules.py‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,40 @@ async def test_time_post_update_uses_offset_when_index_missing_unit(
513513
assert inst.timezone.utcoffset(now) == timedelta(0)
514514

515515

516+
@device_iot
517+
async def test_time_post_update_unsynced_clock_uses_utc(
518+
dev: Device, mocker: MockerFixture
519+
):
520+
"""Fall back to UTC when the device clock is not set.
521+
522+
An unprovisioned device can report e.g. year 2000, which is no valid UTC offset
523+
away from the host time, so the offset-based guess must not be attempted.
524+
"""
525+
from zoneinfo import ZoneInfoNotFoundError
526+
527+
proto = dev.protocol._transport.proto # type: ignore[attr-defined]
528+
for target in ("time", "smartlife.iot.common.timesetting"):
529+
if target in proto:
530+
proto[target]["get_time"] = {
531+
"year": 2000,
532+
"month": 1,
533+
"mday": 1,
534+
"hour": 2,
535+
"min": 45,
536+
"sec": 0,
537+
}
538+
# Force the offset-based path, as when the zone is not available on the host
539+
mocker.patch(
540+
"kasa.iot.modules.time.get_timezone",
541+
new=AsyncMock(side_effect=ZoneInfoNotFoundError("missing on host")),
542+
)
543+
544+
await dev.update()
545+
time_mod = dev.modules[Module.Time]
546+
assert time_mod.timezone is UTC
547+
assert time_mod.time.year == 2000
548+
549+
516550
async def test_time_get_time_exception_returns_none_unit(mocker: MockerFixture):
517551
"""Cover Time.get_time exception path (unit test of iot Time)."""
518552
from kasa.iot.modules.time import Time as TimeModule

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL