Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions kasa/smart/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .temperature import TemperatureSensor
from .temperaturecontrol import TemperatureControl
from .timemodule import TimeModule
from .waterleak import WaterleakSensor

__all__ = [
"AlarmModule",
Expand All @@ -40,4 +41,5 @@
"LightTransitionModule",
"ColorTemperatureModule",
"ColorModule",
"WaterleakSensor",
]
62 changes: 62 additions & 0 deletions kasa/smart/modules/waterleak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Implementation of waterleak module."""

from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING

from ...feature import Feature
from ..smartmodule import SmartModule

if TYPE_CHECKING:
from ..smartdevice import SmartDevice


class WaterleakStatus(Enum):
"""Waterleawk status."""

Normal = "normal"
LeakDetected = "water_leak"
Drying = "water_dry"


class WaterleakSensor(SmartModule):
"""Implementation of waterleak module."""

REQUIRED_COMPONENT = "sensor_alarm"

def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
self._add_feature(
Feature(
device,
"Water leak",
container=self,
attribute_getter="status",
icon="mdi:water",
)
)
self._add_feature(
Feature(
device,
"Water alert",
container=self,
attribute_getter="alert",
icon="mdi:water-alert",
)
)

def query(self) -> dict:
"""Query to execute during the update cycle."""
# Water leak information is contained in the main device info response.
return {}

@property
def status(self) -> WaterleakStatus:
"""Return current humidity in percentage."""
return WaterleakStatus(self._device.sys_info["water_leak_status"])

@property
def alert(self) -> bool:
"""Return true if alarm is active."""
return self._device.sys_info["in_alarm"]
1 change: 1 addition & 0 deletions kasa/smart/smartchilddevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def device_type(self) -> DeviceType:
child_device_map = {
"plug.powerstrip.sub-plug": DeviceType.Plug,
"subg.trigger.temp-hmdt-sensor": DeviceType.Sensor,
"subg.trigger.water-leak-sensor": DeviceType.Sensor,
"kasa.switch.outlet.sub-fan": DeviceType.Fan,
"kasa.switch.outlet.sub-dimmer": DeviceType.Dimmer,
"subg.trv": DeviceType.Thermostat,
Expand Down
42 changes: 42 additions & 0 deletions kasa/tests/smart/modules/test_waterleak.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from enum import Enum

import pytest

from kasa.smart.modules import WaterleakSensor
from kasa.tests.device_fixtures import parametrize

waterleak = parametrize(
"has waterleak", component_filter="sensor_alarm", protocol_filter={"SMART.CHILD"}
)


@waterleak
@pytest.mark.parametrize(
"feature, type",
[
("alert", int),
("status", Enum),
],
)
async def test_waterleak_properties(dev, feature, type):
"""Test that features are registered and work as expected."""
waterleak: WaterleakSensor = dev.modules["WaterleakSensor"]

prop = getattr(waterleak, feature)
assert isinstance(prop, type)

feat = waterleak._module_features[feature]
assert feat.value == prop
assert isinstance(feat.value, type)


@waterleak
async def test_waterleak_features(dev):
"""Test waterleak features."""
waterleak: WaterleakSensor = dev.modules["WaterleakSensor"]

assert "water_leak" in dev.features
assert dev.features["water_leak"].value == waterleak.status

assert "water_alert" in dev.features
assert dev.features["water_alert"].value == waterleak.alert