Skip to content

Add support for contact sensor (T110) #877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 7, 2024
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ The following devices have been tested and confirmed as working. If your device
- **Bulbs**: L510B, L510E, L530E
- **Light Strips**: L900-10, L900-5, L920-5, L930-5
- **Hubs**: H100
- **Hub-Connected Devices<sup>\*\*\*</sup>**: T300, T310, T315
- **Hub-Connected Devices<sup>\*\*\*</sup>**: T110, T300, T310, T315

<!--SUPPORTED_END-->
<sup>\*</sup>&nbsp;&nbsp; Model requires authentication<br>
Expand Down
2 changes: 2 additions & 0 deletions SUPPORTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros

### Hub-Connected Devices

- **T110**
- Hardware: 1.0 (EU) / Firmware: 1.8.0
- **T300**
- Hardware: 1.0 (EU) / Firmware: 1.7.0
- **T310**
Expand Down
2 changes: 2 additions & 0 deletions kasa/smart/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .cloudmodule import CloudModule
from .colormodule import ColorModule
from .colortemp import ColorTemperatureModule
from .contact import ContactSensor
from .devicemodule import DeviceModule
from .energymodule import EnergyModule
from .fanmodule import FanModule
Expand Down Expand Up @@ -45,5 +46,6 @@
"ColorTemperatureModule",
"ColorModule",
"WaterleakSensor",
"ContactSensor",
"FrostProtectionModule",
]
42 changes: 42 additions & 0 deletions kasa/smart/modules/contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Implementation of contact sensor module."""

from __future__ import annotations

from typing import TYPE_CHECKING

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

if TYPE_CHECKING:
from ..smartdevice import SmartDevice


class ContactSensor(SmartModule):
"""Implementation of contact sensor module."""

REQUIRED_COMPONENT = None # we depend on availability of key
REQUIRED_KEY_ON_PARENT = "open"

def __init__(self, device: SmartDevice, module: str):
super().__init__(device, module)
self._add_feature(
Feature(
device,
id="is_open",
name="Open",
container=self,
attribute_getter="is_open",
icon="mdi:door",
category=Feature.Category.Primary,
type=Feature.Type.BinarySensor,
)
)

def query(self) -> dict:
"""Query to execute during the update cycle."""
return {}

@property
def is_open(self):
"""Return True if the contact sensor is open."""
return self._device.sys_info["open"]
1 change: 1 addition & 0 deletions kasa/smart/smartchilddevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def device_type(self) -> DeviceType:
"""Return child device type."""
child_device_map = {
"plug.powerstrip.sub-plug": DeviceType.Plug,
"subg.trigger.contact-sensor": DeviceType.Sensor,
"subg.trigger.temp-hmdt-sensor": DeviceType.Sensor,
"subg.trigger.water-leak-sensor": DeviceType.Sensor,
"kasa.switch.outlet.sub-fan": DeviceType.Fan,
Expand Down
5 changes: 4 additions & 1 deletion kasa/smart/smartdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ async def _initialize_modules(self):
skip_parent_only_modules and mod in WALL_SWITCH_PARENT_ONLY_MODULES
) or mod.__name__ in child_modules_to_skip:
continue
if mod.REQUIRED_COMPONENT in self._components:
if (
mod.REQUIRED_COMPONENT in self._components
or self.sys_info.get(mod.REQUIRED_KEY_ON_PARENT) is not None
):
_LOGGER.debug(
"Found required %s, adding %s to modules.",
mod.REQUIRED_COMPONENT,
Expand Down
18 changes: 13 additions & 5 deletions kasa/smart/smartmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@
"""Base class for SMART modules."""

NAME: str
REQUIRED_COMPONENT: str
#: Module is initialized, if the given component is available
REQUIRED_COMPONENT: str | None = None
#: Module is initialized, if the given key available in the main sysinfo
REQUIRED_KEY_ON_PARENT: str | None = None
#: Query to execute during the main update cycle
QUERY_GETTER_NAME: str

REGISTERED_MODULES: dict[str, type[SmartModule]] = {}

def __init__(self, device: SmartDevice, module: str):
self._device: SmartDevice
super().__init__(device, module)

def __init_subclass__(cls, **kwargs):
assert cls.REQUIRED_COMPONENT is not None # noqa: S101

name = getattr(cls, "NAME", cls.__name__)
_LOGGER.debug("Registering %s" % cls)
cls.REGISTERED_MODULES[name] = cls
Expand Down Expand Up @@ -91,8 +94,13 @@

@property
def supported_version(self) -> int:
"""Return version supported by the device."""
return self._device._components[self.REQUIRED_COMPONENT]
"""Return version supported by the device.

If the module has no required component, this will return -1.
"""
if self.REQUIRED_COMPONENT is not None:
return self._device._components[self.REQUIRED_COMPONENT]
return -1

Check warning on line 103 in kasa/smart/smartmodule.py

View check run for this annotation

Codecov / codecov/patch

kasa/smart/smartmodule.py#L103

Added line #L103 was not covered by tests

async def _check_supported(self) -> bool:
"""Additional check to see if the module is supported by the device.
Expand Down
2 changes: 1 addition & 1 deletion kasa/tests/device_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
}

HUBS_SMART = {"H100", "KH100"}
SENSORS_SMART = {"T310", "T315", "T300"}
SENSORS_SMART = {"T310", "T315", "T300", "T110"}
THERMOSTATS_SMART = {"KE100"}

WITH_EMETER_IOT = {"HS110", "HS300", "KP115", "KP125", *BULBS_IOT}
Expand Down
Loading
Loading