mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 10:44:04 +00:00
Make iot time timezone aware (#1147)
Also makes on_since for iot devices use device time. Changes the return value for device.timezone to be tzinfo instead of a dict.
This commit is contained in:
@@ -10,7 +10,7 @@ class DeviceModule(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "device"
|
||||
|
||||
def _post_update_hook(self):
|
||||
async def _post_update_hook(self):
|
||||
"""Perform actions after a device update.
|
||||
|
||||
Overrides the default behaviour to disable a module if the query returns
|
||||
|
@@ -152,7 +152,7 @@ class Light(SmartModule, LightInterface):
|
||||
"""Return the current light state."""
|
||||
return self._light_state
|
||||
|
||||
def _post_update_hook(self) -> None:
|
||||
async def _post_update_hook(self) -> None:
|
||||
if self._device.is_on is False:
|
||||
state = LightState(light_on=False)
|
||||
else:
|
||||
|
@@ -28,7 +28,7 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
_effect_list: list[str]
|
||||
_scenes_names_to_id: dict[str, str]
|
||||
|
||||
def _post_update_hook(self) -> None:
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Update internal effect state."""
|
||||
# Copy the effects so scene name updates do not update the underlying dict.
|
||||
effects = copy.deepcopy(
|
||||
|
@@ -34,7 +34,7 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
self._state_in_sysinfo = self.SYS_INFO_STATE_KEY in device.sys_info
|
||||
self._brightness_only: bool = False
|
||||
|
||||
def _post_update_hook(self):
|
||||
async def _post_update_hook(self):
|
||||
"""Update the internal presets."""
|
||||
index = 0
|
||||
self._presets = {}
|
||||
|
@@ -90,7 +90,7 @@ class LightTransition(SmartModule):
|
||||
)
|
||||
)
|
||||
|
||||
def _post_update_hook(self) -> None:
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Update the states."""
|
||||
# Assumes any device with state in sysinfo supports on and off and
|
||||
# has maximum values for both.
|
||||
|
@@ -2,10 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
from time import mktime
|
||||
from typing import cast
|
||||
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from ...feature import Feature
|
||||
from ..smartmodule import SmartModule
|
||||
|
||||
@@ -31,18 +33,27 @@ class Time(SmartModule):
|
||||
)
|
||||
|
||||
@property
|
||||
def time(self) -> datetime:
|
||||
"""Return device's current datetime."""
|
||||
def timezone(self) -> tzinfo:
|
||||
"""Return current timezone."""
|
||||
td = timedelta(minutes=cast(float, self.data.get("time_diff")))
|
||||
if self.data.get("region"):
|
||||
tz = timezone(td, str(self.data.get("region")))
|
||||
if region := self.data.get("region"):
|
||||
try:
|
||||
# Zoneinfo will return a DST aware object
|
||||
tz: tzinfo = ZoneInfo(region)
|
||||
except ZoneInfoNotFoundError:
|
||||
tz = timezone(td, region)
|
||||
else:
|
||||
# in case the device returns a blank region this will result in the
|
||||
# tzname being a UTC offset
|
||||
tz = timezone(td)
|
||||
return tz
|
||||
|
||||
@property
|
||||
def time(self) -> datetime:
|
||||
"""Return device's current datetime."""
|
||||
return datetime.fromtimestamp(
|
||||
cast(float, self.data.get("timestamp")),
|
||||
tz=tz,
|
||||
tz=self.timezone,
|
||||
)
|
||||
|
||||
async def set_time(self, dt: datetime):
|
||||
|
@@ -61,7 +61,7 @@ class SmartChildDevice(SmartDevice):
|
||||
self._last_update = await self.protocol.query(req)
|
||||
|
||||
for module in self.modules.values():
|
||||
self._handle_module_post_update(
|
||||
await self._handle_module_post_update(
|
||||
module, now, had_query=module in module_queries
|
||||
)
|
||||
self._last_update_time = now
|
||||
|
@@ -6,8 +6,8 @@ import base64
|
||||
import logging
|
||||
import time
|
||||
from collections.abc import Mapping, Sequence
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, cast
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from ..aestransport import AesTransport
|
||||
from ..device import Device, WifiNetwork
|
||||
@@ -168,7 +168,7 @@ class SmartDevice(Device):
|
||||
await self._initialize_modules()
|
||||
# Run post update for the cloud module
|
||||
if cloud_mod := self.modules.get(Module.Cloud):
|
||||
self._handle_module_post_update(cloud_mod, now, had_query=True)
|
||||
await self._handle_module_post_update(cloud_mod, now, had_query=True)
|
||||
|
||||
resp = await self._modular_update(first_update, now)
|
||||
|
||||
@@ -195,7 +195,7 @@ class SmartDevice(Device):
|
||||
updated = self._last_update if first_update else resp
|
||||
_LOGGER.debug("Update completed %s: %s", self.host, list(updated.keys()))
|
||||
|
||||
def _handle_module_post_update(
|
||||
async def _handle_module_post_update(
|
||||
self, module: SmartModule, update_time: float, had_query: bool
|
||||
):
|
||||
if module.disabled:
|
||||
@@ -203,7 +203,7 @@ class SmartDevice(Device):
|
||||
if had_query:
|
||||
module._last_update_time = update_time
|
||||
try:
|
||||
module._post_update_hook()
|
||||
await module._post_update_hook()
|
||||
module._set_error(None)
|
||||
except Exception as ex:
|
||||
# Only set the error if a query happened.
|
||||
@@ -260,7 +260,7 @@ class SmartDevice(Device):
|
||||
|
||||
# Call handle update for modules that want to update internal data
|
||||
for module in self._modules.values():
|
||||
self._handle_module_post_update(
|
||||
await self._handle_module_post_update(
|
||||
module, update_time, had_query=module in module_queries
|
||||
)
|
||||
|
||||
@@ -516,10 +516,11 @@ class SmartDevice(Device):
|
||||
return self._on_since
|
||||
|
||||
@property
|
||||
def timezone(self) -> dict:
|
||||
def timezone(self) -> tzinfo:
|
||||
"""Return the timezone and time_difference."""
|
||||
ti = self.time
|
||||
return {"timezone": ti.tzname()}
|
||||
if TYPE_CHECKING:
|
||||
assert self.time.tzinfo
|
||||
return self.time.tzinfo
|
||||
|
||||
@property
|
||||
def hw_info(self) -> dict:
|
||||
|
@@ -121,7 +121,7 @@ class SmartModule(Module):
|
||||
"""Name of the module."""
|
||||
return getattr(self, "NAME", self.__class__.__name__)
|
||||
|
||||
def _post_update_hook(self): # noqa: B027
|
||||
async def _post_update_hook(self): # noqa: B027
|
||||
"""Perform actions after a device update.
|
||||
|
||||
Any modules overriding this should ensure that self.data is
|
||||
|
Reference in New Issue
Block a user