From a87926f9d73262b9a8763ce342d46ec685108d7d Mon Sep 17 00:00:00 2001 From: "Teemu R." Date: Sun, 30 Nov 2025 20:07:54 +0100 Subject: [PATCH] waterleaksensor: use parent's Time for alert timestamp (#1614) We do not, by design, add Time module for hub's children. This has a side-effect that we need to fallback to the parent's time module to allow presenting the correct timestamp for the last alert. --- kasa/smart/modules/waterleaksensor.py | 15 ++++++++++++++- tests/smart/modules/test_waterleak.py | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/kasa/smart/modules/waterleaksensor.py b/kasa/smart/modules/waterleaksensor.py index b6f01017..fbf9e404 100644 --- a/kasa/smart/modules/waterleaksensor.py +++ b/kasa/smart/modules/waterleaksensor.py @@ -4,8 +4,10 @@ from __future__ import annotations from datetime import datetime from enum import Enum +from typing import TYPE_CHECKING from ...feature import Feature +from ...interfaces.time import Time from ..smartmodule import Module, SmartModule @@ -76,6 +78,17 @@ class WaterleakSensor(SmartModule): """Return true if alarm is active.""" return self._device.sys_info["in_alarm"] + @property + def _time_module(self) -> Time: + """Return time module from the parent for timestamp calculation.""" + parent = self._device.parent + if TYPE_CHECKING: + from ..smartdevice import SmartDevice + + assert isinstance(parent, SmartDevice) + + return parent.modules[Module.Time] + @property def alert_timestamp(self) -> datetime | None: """Return timestamp of the last leak trigger.""" @@ -84,5 +97,5 @@ class WaterleakSensor(SmartModule): return None ts = self._device.sys_info["trigger_timestamp"] - tz = self._device.modules[Module.Time].timezone + tz = self._time_module.timezone return datetime.fromtimestamp(ts, tz=tz) diff --git a/tests/smart/modules/test_waterleak.py b/tests/smart/modules/test_waterleak.py index 1821e6e0..afae7dda 100644 --- a/tests/smart/modules/test_waterleak.py +++ b/tests/smart/modules/test_waterleak.py @@ -5,6 +5,7 @@ import pytest from kasa.smart.modules import WaterleakSensor +from ...conftest import get_device_for_fixture_protocol from ...device_fixtures import parametrize waterleak = parametrize( @@ -12,6 +13,12 @@ waterleak = parametrize( ) +@pytest.fixture +async def parent(request): + """Get a dummy parent for tz tests.""" + return await get_device_for_fixture_protocol("H100(EU)_1.0_1.5.5.json", "SMART") + + @waterleak @pytest.mark.parametrize( ("feature", "prop_name", "type"), @@ -21,8 +28,9 @@ waterleak = parametrize( ("water_leak", "status", Enum), ], ) -async def test_waterleak_properties(dev, feature, prop_name, type): +async def test_waterleak_properties(dev, parent, feature, prop_name, type): """Test that features are registered and work as expected.""" + dev._parent = parent waterleak: WaterleakSensor = dev.modules["WaterleakSensor"] prop = getattr(waterleak, prop_name) @@ -34,8 +42,9 @@ async def test_waterleak_properties(dev, feature, prop_name, type): @waterleak -async def test_waterleak_features(dev): +async def test_waterleak_features(dev, parent): """Test waterleak features.""" + dev._parent = parent waterleak: WaterleakSensor = dev.modules["WaterleakSensor"] assert "water_leak" in dev.features @@ -43,3 +52,6 @@ async def test_waterleak_features(dev): assert "water_alert" in dev.features assert dev.features["water_alert"].value == waterleak.alert + + assert "water_alert_timestamp" in dev.features + assert dev.features["water_alert_timestamp"].value == waterleak.alert_timestamp