diff --git a/kasa/iot/iotstrip.py b/kasa/iot/iotstrip.py index 64ddf0a0..7984ffb7 100755 --- a/kasa/iot/iotstrip.py +++ b/kasa/iot/iotstrip.py @@ -7,6 +7,9 @@ from collections import defaultdict from datetime import datetime, timedelta from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from datetime import tzinfo + from ..device_type import DeviceType from ..deviceconfig import DeviceConfig from ..emeterstatus import EmeterStatus @@ -349,6 +352,8 @@ class IotStripPlug(IotPlug): self.add_module(Module.IotAntitheft, Antitheft(self, "anti_theft")) self.add_module(Module.IotSchedule, Schedule(self, "schedule")) self.add_module(Module.IotCountdown, Countdown(self, "countdown")) + # Note: do not add a Time module to the child; time is device-level. + # Child exposes time/timezone by delegating to the parent. async def _initialize_features(self) -> None: """Initialize common features.""" @@ -441,6 +446,18 @@ class IotStripPlug(IotPlug): """ return False + @property # type: ignore + @requires_update + def time(self) -> datetime: + """Return current time, delegated from the parent strip.""" + return self._parent.time + + @property # type: ignore + @requires_update + def timezone(self) -> tzinfo: + """Return timezone, delegated from the parent strip.""" + return self._parent.timezone + @property # type: ignore @requires_update def device_id(self) -> str: diff --git a/tests/device_fixtures.py b/tests/device_fixtures.py index a6cef66c..7c9ab2a2 100644 --- a/tests/device_fixtures.py +++ b/tests/device_fixtures.py @@ -310,6 +310,11 @@ bulb = parametrize_combine([bulb_smart, bulb_iot]) strip_iot = parametrize( "strip devices iot", model_filter=STRIPS_IOT, protocol_filter={"IOT"} ) +strip_emeter_iot = parametrize( + "strip devices iot with emeter", + model_filter=STRIPS_IOT & WITH_EMETER_IOT, + protocol_filter={"IOT"}, +) strip_smart = parametrize( "strip devices smart", model_filter=STRIPS_SMART, protocol_filter={"SMART"} ) diff --git a/tests/iot/test_iotstrip.py b/tests/iot/test_iotstrip.py new file mode 100644 index 00000000..50c1df61 --- /dev/null +++ b/tests/iot/test_iotstrip.py @@ -0,0 +1,44 @@ +from unittest.mock import AsyncMock + +from kasa import Module +from tests.conftest import strip_emeter_iot, strip_iot + + +@strip_iot +async def test_strip_update_and_child_update_behaviors(dev): + await dev.update() + await dev.update(update_children=False) + + assert dev.children, "Expected strip device to have children" + + child = dev.children[0] + await child.update(update_children=False) + + assert getattr(child, "_features", None) + + +@strip_iot +async def test_strip_child_delegated_properties(dev): + await dev.update() + child = dev.children[0] + + assert child.led is False + assert child.time == dev.time + assert child.timezone == dev.timezone + + na = child.next_action + assert isinstance(na, dict) + assert "type" in na + + +@strip_emeter_iot +async def test_strip_emeter_erase_stats(dev, mocker): + await dev.update() + + for child in dev.children: + energy = child.modules.get(Module.Energy) + if energy: + mocker.patch.object(energy, "erase_stats", AsyncMock(return_value={})) + + res = await dev.modules[Module.Energy].erase_stats() + assert res == {}