Fix SMARTCAM Time module and update tests (#1659)

Update SmartCam time handling to apply timezone-only changes with explicit warnings, while keeping common time and CLI tests focused on devices that support setting clock time and adding SmartCam-specific coverage for the special behavior.
This commit is contained in:
ZeliardM
2026-07-10 20:38:12 -04:00
committed by GitHub
parent 8c66d0a29d
commit b2206841dd
5 changed files with 160 additions and 21 deletions

View File

@@ -2,16 +2,20 @@
from __future__ import annotations
from datetime import UTC, datetime, tzinfo
import logging
from datetime import UTC, datetime, timedelta, tzinfo
from typing import cast
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from ...cachedzoneinfo import CachedZoneInfo
from ...exceptions import KasaException
from ...feature import Feature
from ...interfaces import Time as TimeInterface
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class Time(SmartCamModule, TimeInterface):
"""Implementation of device_local_time."""
@@ -76,17 +80,35 @@ class Time(SmartCamModule, TimeInterface):
@allow_update_after
async def set_time(self, dt: datetime) -> dict:
"""Set device time."""
if not dt.tzinfo:
timestamp = dt.replace(tzinfo=self.timezone).timestamp()
else:
timestamp = dt.timestamp()
"""Set device timezone derived from the datetime."""
_LOGGER.warning(
"SmartCam devices do not support setting clock time directly; "
"only timezone settings will be updated."
)
timezone = dt.tzinfo or self.timezone
if not isinstance(timezone, ZoneInfo):
raise KasaException(
"SmartCam devices can only update timezone using zoneinfo "
"timezones; setting clock time is not supported."
)
lt = datetime.fromtimestamp(timestamp).isoformat().replace("T", " ")
params = {"seconds_from_1970": int(timestamp), "local_time": lt}
# Doesn't seem to update the time, perhaps because timing_mode is ntp
res = await self.call("setTimezone", {"system": {"clock_status": params}})
if (zinfo := dt.tzinfo) and isinstance(zinfo, ZoneInfo):
tz_params = {"zone_id": zinfo.key}
res = await self.call("setTimezone", {"system": {"basic": tz_params}})
return res
utc_offset = cast(timedelta | None, timezone.utcoffset(dt))
params: dict[str, str] = {
"timezone": self._format_utc_offset(utc_offset),
"zone_id": timezone.key,
}
return await self.call("setTimezone", {"system": {"basic": params}})
@staticmethod
def _format_utc_offset(offset: timedelta | None) -> str:
"""Format a timedelta offset as UTC+HH:MM/UTC-HH:MM."""
if offset is None:
offset = timedelta(0)
total_seconds = int(offset.total_seconds())
sign = "+" if total_seconds >= 0 else "-"
total_seconds = abs(total_seconds)
hours, remainder = divmod(total_seconds, 3600)
minutes = remainder // 60
return f"UTC{sign}{hours:02d}:{minutes:02d}"