Extend smartcam detection support (#1552)

New detection added with tests:

- bark detection
- glass detection
- line crossing detection
- meow detection
- vehicle detection
This commit is contained in:
Gabriele Pongelli
2025-08-25 15:20:33 +02:00
committed by GitHub
parent e21ab90e96
commit 0cd0434160
23 changed files with 1549 additions and 379 deletions

View File

@@ -1,6 +1,7 @@
"""Package for supporting tapo-branded cameras."""
from .detectionmodule import DetectionModule
from .smartcamchild import SmartCamChild
from .smartcamdevice import SmartCamDevice
__all__ = ["SmartCamDevice", "SmartCamChild"]
__all__ = ["SmartCamDevice", "SmartCamChild", "DetectionModule"]

View File

@@ -0,0 +1,58 @@
"""SmartCamModule base class for all detections."""
from __future__ import annotations
import logging
from kasa.feature import Feature
from kasa.smart.smartmodule import allow_update_after
from kasa.smartcam.smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class DetectionModule(SmartCamModule):
"""SmartCamModule base class for all detections."""
#: Feature ID, filled by inheriting class
DETECTION_FEATURE_ID: str = ""
#: User-friendly short description, filled by inheriting class
DETECTION_FEATURE_NAME: str = ""
#: Feature setter method name, filled by inheriting class
QUERY_SETTER_NAME: str = ""
#: Feature section name, filled by inheriting class
QUERY_SET_SECTION_NAME: str = ""
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id=self.DETECTION_FEATURE_ID,
name=self.DETECTION_FEATURE_NAME,
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the detection enabled state."""
return self.data[self.QUERY_SECTION_NAMES]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
self.QUERY_SETTER_NAME,
self.QUERY_MODULE_NAME,
self.QUERY_SET_SECTION_NAME,
params,
)

View File

@@ -2,31 +2,40 @@
from .alarm import Alarm
from .babycrydetection import BabyCryDetection
from .barkdetection import BarkDetection
from .battery import Battery
from .camera import Camera
from .childdevice import ChildDevice
from .childsetup import ChildSetup
from .device import DeviceModule
from .glassdetection import GlassDetection
from .homekit import HomeKit
from .led import Led
from .lensmask import LensMask
from .linecrossingdetection import LineCrossingDetection
from .matter import Matter
from .meowdetection import MeowDetection
from .motiondetection import MotionDetection
from .pantilt import PanTilt
from .persondetection import PersonDetection
from .petdetection import PetDetection
from .tamperdetection import TamperDetection
from .time import Time
from .vehicledetection import VehicleDetection
__all__ = [
"Alarm",
"BabyCryDetection",
"BarkDetection",
"Battery",
"Camera",
"ChildDevice",
"ChildSetup",
"DeviceModule",
"GlassDetection",
"Led",
"LineCrossingDetection",
"MeowDetection",
"PanTilt",
"PersonDetection",
"PetDetection",
@@ -36,4 +45,5 @@ __all__ = [
"MotionDetection",
"LensMask",
"TamperDetection",
"VehicleDetection",
]

View File

@@ -4,14 +4,12 @@ from __future__ import annotations
import logging
from ...feature import Feature
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class BabyCryDetection(SmartCamModule):
class BabyCryDetection(DetectionModule):
"""Implementation of baby cry detection module."""
REQUIRED_COMPONENT = "babyCryDetection"
@@ -20,30 +18,7 @@ class BabyCryDetection(SmartCamModule):
QUERY_MODULE_NAME = "sound_detection"
QUERY_SECTION_NAMES = "bcd"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="baby_cry_detection",
name="Baby cry detection",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the baby cry detection enabled state."""
return self.data["bcd"]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the baby cry detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
"setBCDConfig", self.QUERY_MODULE_NAME, "bcd", params
)
DETECTION_FEATURE_ID = "baby_cry_detection"
DETECTION_FEATURE_NAME = "Baby cry detection"
QUERY_SETTER_NAME = "setBCDConfig"
QUERY_SET_SECTION_NAME = "bcd"

View File

@@ -0,0 +1,24 @@
"""Implementation of bark detection module."""
from __future__ import annotations
import logging
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class BarkDetection(DetectionModule):
"""Implementation of bark detection module."""
REQUIRED_COMPONENT = "barkDetection"
QUERY_GETTER_NAME = "getBarkDetectionConfig"
QUERY_MODULE_NAME = "bark_detection"
QUERY_SECTION_NAMES = "detection"
DETECTION_FEATURE_ID = "bark_detection"
DETECTION_FEATURE_NAME = "Bark detection"
QUERY_SETTER_NAME = "setBarkDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -0,0 +1,24 @@
"""Implementation of glass detection module."""
from __future__ import annotations
import logging
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class GlassDetection(DetectionModule):
"""Implementation of glass detection module."""
REQUIRED_COMPONENT = "glassDetection"
QUERY_GETTER_NAME = "getGlassDetectionConfig"
QUERY_MODULE_NAME = "glass_detection"
QUERY_SECTION_NAMES = "detection"
DETECTION_FEATURE_ID = "glass_detection"
DETECTION_FEATURE_NAME = "Glass detection"
QUERY_SETTER_NAME = "setGlassDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -0,0 +1,24 @@
"""Implementation of line crossing detection module."""
from __future__ import annotations
import logging
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class LineCrossingDetection(DetectionModule):
"""Implementation of line crossing detection module."""
REQUIRED_COMPONENT = "linecrossingDetection"
QUERY_GETTER_NAME = "getLinecrossingDetectionConfig"
QUERY_MODULE_NAME = "linecrossing_detection"
QUERY_SECTION_NAMES = "detection"
DETECTION_FEATURE_ID = "line_crossing_detection"
DETECTION_FEATURE_NAME = "Line crossing detection"
QUERY_SETTER_NAME = "setLinecrossingDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -0,0 +1,24 @@
"""Implementation of meow detection module."""
from __future__ import annotations
import logging
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class MeowDetection(DetectionModule):
"""Implementation of meow detection module."""
REQUIRED_COMPONENT = "meowDetection"
QUERY_GETTER_NAME = "getMeowDetectionConfig"
QUERY_MODULE_NAME = "meow_detection"
QUERY_SECTION_NAMES = "detection"
DETECTION_FEATURE_ID = "meow_detection"
DETECTION_FEATURE_NAME = "Meow detection"
QUERY_SETTER_NAME = "setMeowDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -4,14 +4,12 @@ from __future__ import annotations
import logging
from ...feature import Feature
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class MotionDetection(SmartCamModule):
class MotionDetection(DetectionModule):
"""Implementation of motion detection module."""
REQUIRED_COMPONENT = "detection"
@@ -20,30 +18,7 @@ class MotionDetection(SmartCamModule):
QUERY_MODULE_NAME = "motion_detection"
QUERY_SECTION_NAMES = "motion_det"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="motion_detection",
name="Motion detection",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the motion detection enabled state."""
return self.data["motion_det"]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the motion detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
"setDetectionConfig", self.QUERY_MODULE_NAME, "motion_det", params
)
DETECTION_FEATURE_ID = "motion_detection"
DETECTION_FEATURE_NAME = "Motion detection"
QUERY_SETTER_NAME = "setDetectionConfig"
QUERY_SET_SECTION_NAME = "motion_det"

View File

@@ -4,14 +4,12 @@ from __future__ import annotations
import logging
from ...feature import Feature
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class PersonDetection(SmartCamModule):
class PersonDetection(DetectionModule):
"""Implementation of person detection module."""
REQUIRED_COMPONENT = "personDetection"
@@ -20,30 +18,7 @@ class PersonDetection(SmartCamModule):
QUERY_MODULE_NAME = "people_detection"
QUERY_SECTION_NAMES = "detection"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="person_detection",
name="Person detection",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the person detection enabled state."""
return self.data["detection"]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the person detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
"setPersonDetectionConfig", self.QUERY_MODULE_NAME, "detection", params
)
DETECTION_FEATURE_ID = "person_detection"
DETECTION_FEATURE_NAME = "Person detection"
QUERY_SETTER_NAME = "setPersonDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -4,14 +4,12 @@ from __future__ import annotations
import logging
from ...feature import Feature
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class PetDetection(SmartCamModule):
class PetDetection(DetectionModule):
"""Implementation of pet detection module."""
REQUIRED_COMPONENT = "petDetection"
@@ -20,30 +18,7 @@ class PetDetection(SmartCamModule):
QUERY_MODULE_NAME = "pet_detection"
QUERY_SECTION_NAMES = "detection"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="pet_detection",
name="Pet detection",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the pet detection enabled state."""
return self.data["detection"]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the pet detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
"setPetDetectionConfig", self.QUERY_MODULE_NAME, "detection", params
)
DETECTION_FEATURE_ID = "pet_detection"
DETECTION_FEATURE_NAME = "Pet detection"
QUERY_SETTER_NAME = "setPetDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -4,14 +4,12 @@ from __future__ import annotations
import logging
from ...feature import Feature
from ...smart.smartmodule import allow_update_after
from ..smartcammodule import SmartCamModule
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class TamperDetection(SmartCamModule):
class TamperDetection(DetectionModule):
"""Implementation of tamper detection module."""
REQUIRED_COMPONENT = "tamperDetection"
@@ -20,30 +18,7 @@ class TamperDetection(SmartCamModule):
QUERY_MODULE_NAME = "tamper_detection"
QUERY_SECTION_NAMES = "tamper_det"
def _initialize_features(self) -> None:
"""Initialize features after the initial update."""
self._add_feature(
Feature(
self._device,
id="tamper_detection",
name="Tamper detection",
container=self,
attribute_getter="enabled",
attribute_setter="set_enabled",
type=Feature.Type.Switch,
category=Feature.Category.Config,
)
)
@property
def enabled(self) -> bool:
"""Return the tamper detection enabled state."""
return self.data["tamper_det"]["enabled"] == "on"
@allow_update_after
async def set_enabled(self, enable: bool) -> dict:
"""Set the tamper detection enabled state."""
params = {"enabled": "on" if enable else "off"}
return await self._device._query_setter_helper(
"setTamperDetectionConfig", self.QUERY_MODULE_NAME, "tamper_det", params
)
DETECTION_FEATURE_ID = "tamper_detection"
DETECTION_FEATURE_NAME = "Tamper detection"
QUERY_SETTER_NAME = "setTamperDetectionConfig"
QUERY_SET_SECTION_NAME = "tamper_det"

View File

@@ -0,0 +1,24 @@
"""Implementation of vehicle detection module."""
from __future__ import annotations
import logging
from kasa.smartcam.detectionmodule import DetectionModule
_LOGGER = logging.getLogger(__name__)
class VehicleDetection(DetectionModule):
"""Implementation of vehicle detection module."""
REQUIRED_COMPONENT = "vehicleDetection"
QUERY_GETTER_NAME = "getVehicleDetectionConfig"
QUERY_MODULE_NAME = "vehicle_detection"
QUERY_SECTION_NAMES = "detection"
DETECTION_FEATURE_ID = "vehicle_detection"
DETECTION_FEATURE_NAME = "Vehicle detection"
QUERY_SETTER_NAME = "setVehicleDetectionConfig"
QUERY_SET_SECTION_NAME = "detection"

View File

@@ -36,6 +36,22 @@ class SmartCamModule(SmartModule):
"BabyCryDetection"
)
SmartCamLineCrossingDetection: Final[ModuleName[modules.LineCrossingDetection]] = (
ModuleName("LineCrossingDetection")
)
SmartCamBarkDetection: Final[ModuleName[modules.BarkDetection]] = ModuleName(
"BarkDetection"
)
SmartCamGlassDetection: Final[ModuleName[modules.GlassDetection]] = ModuleName(
"GlassDetection"
)
SmartCamMeowDetection: Final[ModuleName[modules.MeowDetection]] = ModuleName(
"MeowDetection"
)
SmartCamVehicleDetection: Final[ModuleName[modules.VehicleDetection]] = ModuleName(
"VehicleDetection"
)
SmartCamBattery: Final[ModuleName[modules.Battery]] = ModuleName("Battery")
SmartCamDeviceModule: Final[ModuleName[modules.DeviceModule]] = ModuleName(