Add support for T100 motion sensor (#1079)

Add support for T100 motion sensor.
Thanks to @DarthSonic for the fixture file!
This commit is contained in:
Teemu R. 2024-07-24 15:48:33 +02:00 committed by GitHub
parent dc0aedad20
commit 055bbcc0c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 608 additions and 2 deletions

View File

@ -194,7 +194,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>**: T110, T300, T310, T315
- **Hub-Connected Devices<sup>\*\*\*</sup>**: T100, T110, T300, T310, T315
<!--SUPPORTED_END-->
<sup>\*</sup>&nbsp;&nbsp; Model requires authentication<br>

View File

@ -231,6 +231,8 @@ All Tapo devices require authentication.<br>Hub-Connected Devices may work acros
### Hub-Connected Devices
- **T100**
- Hardware: 1.0 (EU) / Firmware: 1.12.0
- **T110**
- Hardware: 1.0 (EU) / Firmware: 1.8.0
- **T300**

View File

@ -111,6 +111,7 @@ class Module(ABC):
LightTransition: Final[ModuleName[smart.LightTransition]] = ModuleName(
"LightTransition"
)
MotionSensor: Final[ModuleName[smart.MotionSensor]] = ModuleName("MotionSensor")
ReportMode: Final[ModuleName[smart.ReportMode]] = ModuleName("ReportMode")
SmartLightEffect: Final[ModuleName[smart.SmartLightEffect]] = ModuleName(
"LightEffect"

View File

@ -22,6 +22,7 @@ from .lighteffect import LightEffect
from .lightpreset import LightPreset
from .lightstripeffect import LightStripEffect
from .lighttransition import LightTransition
from .motionsensor import MotionSensor
from .reportmode import ReportMode
from .temperaturecontrol import TemperatureControl
from .temperaturesensor import TemperatureSensor
@ -54,6 +55,7 @@ __all__ = [
"Color",
"WaterleakSensor",
"ContactSensor",
"MotionSensor",
"FrostProtection",
"SmartLightEffect",
]

View File

@ -0,0 +1,36 @@
"""Implementation of motion sensor module."""
from __future__ import annotations
from ...feature import Feature
from ..smartmodule import SmartModule
class MotionSensor(SmartModule):
"""Implementation of motion sensor module."""
REQUIRED_COMPONENT = "sensitivity"
def _initialize_features(self):
"""Initialize features."""
self._add_feature(
Feature(
self._device,
id="motion_detected",
name="Motion detected",
container=self,
attribute_getter="motion_detected",
icon="mdi:motion-sensor",
category=Feature.Category.Primary,
type=Feature.Type.BinarySensor,
)
)
def query(self) -> dict:
"""Query to execute during the update cycle."""
return {}
@property
def motion_detected(self):
"""Return True if the motion has been detected."""
return self._device.sys_info["detected"]

View File

@ -117,7 +117,7 @@ DIMMERS = {
}
HUBS_SMART = {"H100", "KH100"}
SENSORS_SMART = {"T310", "T315", "T300", "T110"}
SENSORS_SMART = {"T310", "T315", "T300", "T100", "T110"}
THERMOSTATS_SMART = {"KE100"}
WITH_EMETER_IOT = {"HS110", "HS300", "KP115", "KP125", *BULBS_IOT}

View File

@ -0,0 +1,537 @@
{
"component_nego": {
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "trigger_log",
"ver_code": 1
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 1
},
{
"id": "localSmart",
"ver_code": 1
},
{
"id": "battery_detect",
"ver_code": 1
},
{
"id": "sensitivity",
"ver_code": 1
}
]
},
"get_connect_cloud_state": {
"status": 0
},
"get_device_info": {
"at_low_battery": false,
"avatar": "sensor",
"bind_count": 1,
"category": "subg.trigger.motion-sensor",
"detected": false,
"device_id": "SCRUBBED_CHILD_DEVICE_ID_3",
"fw_ver": "1.12.0 Build 230512 Rel.103011",
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"jamming_rssi": -118,
"jamming_signal_level": 1,
"lastOnboardingTimestamp": 1703860126,
"mac": "E4FAC4000000",
"model": "T100",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"parent_device_id": "0000000000000000000000000000000000000000",
"region": "Europe/Berlin",
"report_interval": 60,
"rssi": -73,
"signal_level": 2,
"specs": "EU",
"status": "online",
"status_follow_edge": false,
"type": "SMART.TAPOSENSOR"
},
"get_fw_download_state": {
"cloud_cache_seconds": 1,
"download_progress": 0,
"reboot_time": 5,
"status": 0,
"upgrade_time": 5
},
"get_latest_fw": {
"fw_size": 0,
"fw_ver": "1.12.0 Build 230512 Rel.103011",
"hw_id": "",
"need_to_upgrade": false,
"oem_id": "",
"release_date": "",
"release_note": "",
"type": 0
},
"get_temp_humidity_records": {
"local_time": 1721645923,
"past24h_humidity": [
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000
],
"past24h_humidity_exception": [
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000
],
"past24h_temp": [
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000
],
"past24h_temp_exception": [
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000,
-1000
],
"temp_unit": "celsius"
},
"get_trigger_logs": {
"logs": [
{
"event": "motion",
"eventId": "f883b62c-e18f-30ef-883b-62ce18f30ef8",
"id": 28763,
"timestamp": 1721643865
},
{
"event": "motion",
"eventId": "c5157545-55d5-157d-4157-54555d5157d4",
"id": 28748,
"timestamp": 1721630821
},
{
"event": "motion",
"eventId": "1b587961-edab-08d1-b587-961edab08d1b",
"id": 28746,
"timestamp": 1721629441
},
{
"event": "motion",
"eventId": "8ac5e271-3894-c269-bc5e-2713894c269b",
"id": 28738,
"timestamp": 1721622777
},
{
"event": "motion",
"eventId": "1ef8037e-c097-bc21-ef80-37ec097bc21e",
"id": 28722,
"timestamp": 1721596432
}
],
"start_id": 28763,
"sum": 86
}
}

View File

@ -0,0 +1,28 @@
import pytest
from kasa import Module, SmartDevice
from kasa.tests.device_fixtures import parametrize
motion = parametrize(
"is motion sensor", model_filter="T100", protocol_filter={"SMART.CHILD"}
)
@motion
@pytest.mark.parametrize(
"feature, type",
[
("motion_detected", bool),
],
)
async def test_motion_features(dev: SmartDevice, feature, type):
"""Test that features are registered and work as expected."""
motion = dev.modules.get(Module.MotionSensor)
assert motion is not None
prop = getattr(motion, feature)
assert isinstance(prop, type)
feat = dev.features[feature]
assert feat.value == prop
assert isinstance(feat.value, type)