Add smartcam detection modules (#1389)
Some checks are pending
CI / Perform linting checks (3.13) (push) Waiting to run
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Blocked by required conditions
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Blocked by required conditions
CodeQL checks / Analyze (python) (push) Waiting to run

- Motion detection
- Person detection
- Tamper detection
- Baby Cry Detection
This commit is contained in:
Steven B. 2024-12-19 23:22:08 +00:00 committed by GitHub
parent b5f49a3c8a
commit d890b0a3ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 386 additions and 0 deletions

View File

@ -1,6 +1,7 @@
"""Modules for SMARTCAM devices.""" """Modules for SMARTCAM devices."""
from .alarm import Alarm from .alarm import Alarm
from .babycrydetection import BabyCryDetection
from .camera import Camera from .camera import Camera
from .childdevice import ChildDevice from .childdevice import ChildDevice
from .device import DeviceModule from .device import DeviceModule
@ -8,18 +9,25 @@ from .homekit import HomeKit
from .led import Led from .led import Led
from .lensmask import LensMask from .lensmask import LensMask
from .matter import Matter from .matter import Matter
from .motiondetection import MotionDetection
from .pantilt import PanTilt from .pantilt import PanTilt
from .persondetection import PersonDetection
from .tamperdetection import TamperDetection
from .time import Time from .time import Time
__all__ = [ __all__ = [
"Alarm", "Alarm",
"BabyCryDetection",
"Camera", "Camera",
"ChildDevice", "ChildDevice",
"DeviceModule", "DeviceModule",
"Led", "Led",
"PanTilt", "PanTilt",
"PersonDetection",
"Time", "Time",
"HomeKit", "HomeKit",
"Matter", "Matter",
"MotionDetection",
"LensMask", "LensMask",
"TamperDetection",
] ]

View File

@ -0,0 +1,47 @@
"""Implementation of baby cry detection module."""
from __future__ import annotations
import logging
from ...feature import Feature
from ..smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class BabyCryDetection(SmartCamModule):
"""Implementation of baby cry detection module."""
REQUIRED_COMPONENT = "babyCryDetection"
QUERY_GETTER_NAME = "getBCDConfig"
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.Primary,
)
)
@property
def enabled(self) -> bool:
"""Return the baby cry detection enabled state."""
return self.data["bcd"]["enabled"] == "on"
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
)

View File

@ -0,0 +1,47 @@
"""Implementation of motion detection module."""
from __future__ import annotations
import logging
from ...feature import Feature
from ..smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class MotionDetection(SmartCamModule):
"""Implementation of motion detection module."""
REQUIRED_COMPONENT = "detection"
QUERY_GETTER_NAME = "getDetectionConfig"
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.Primary,
)
)
@property
def enabled(self) -> bool:
"""Return the motion detection enabled state."""
return self.data["motion_det"]["enabled"] == "on"
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
)

View File

@ -0,0 +1,47 @@
"""Implementation of person detection module."""
from __future__ import annotations
import logging
from ...feature import Feature
from ..smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class PersonDetection(SmartCamModule):
"""Implementation of person detection module."""
REQUIRED_COMPONENT = "personDetection"
QUERY_GETTER_NAME = "getPersonDetectionConfig"
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.Primary,
)
)
@property
def enabled(self) -> bool:
"""Return the person detection enabled state."""
return self.data["detection"]["enabled"] == "on"
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
)

View File

@ -0,0 +1,47 @@
"""Implementation of tamper detection module."""
from __future__ import annotations
import logging
from ...feature import Feature
from ..smartcammodule import SmartCamModule
_LOGGER = logging.getLogger(__name__)
class TamperDetection(SmartCamModule):
"""Implementation of tamper detection module."""
REQUIRED_COMPONENT = "tamperDetection"
QUERY_GETTER_NAME = "getTamperDetectionConfig"
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.Primary,
)
)
@property
def enabled(self) -> bool:
"""Return the tamper detection enabled state."""
return self.data["tamper_det"]["enabled"] == "on"
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
)

View File

@ -20,6 +20,18 @@ class SmartCamModule(SmartModule):
"""Base class for SMARTCAM modules.""" """Base class for SMARTCAM modules."""
SmartCamAlarm: Final[ModuleName[modules.Alarm]] = ModuleName("SmartCamAlarm") SmartCamAlarm: Final[ModuleName[modules.Alarm]] = ModuleName("SmartCamAlarm")
SmartCamMotionDetection: Final[ModuleName[modules.MotionDetection]] = ModuleName(
"MotionDetection"
)
SmartCamPersonDetection: Final[ModuleName[modules.PersonDetection]] = ModuleName(
"PersonDetection"
)
SmartCamTamperDetection: Final[ModuleName[modules.TamperDetection]] = ModuleName(
"TamperDetection"
)
SmartCamBabyCryDetection: Final[ModuleName[modules.BabyCryDetection]] = ModuleName(
"BabyCryDetection"
)
#: Module name to be queried #: Module name to be queried
QUERY_MODULE_NAME: str QUERY_MODULE_NAME: str

View File

@ -0,0 +1,45 @@
"""Tests for smartcam baby cry detection module."""
from __future__ import annotations
from kasa import Device
from kasa.smartcam.smartcammodule import SmartCamModule
from ...device_fixtures import parametrize
babycrydetection = parametrize(
"has babycry detection",
component_filter="babyCryDetection",
protocol_filter={"SMARTCAM"},
)
@babycrydetection
async def test_babycrydetection(dev: Device):
"""Test device babycry detection."""
babycry = dev.modules.get(SmartCamModule.SmartCamBabyCryDetection)
assert babycry
bcde_feat = dev.features.get("baby_cry_detection")
assert bcde_feat
original_enabled = babycry.enabled
try:
await babycry.set_enabled(not original_enabled)
await dev.update()
assert babycry.enabled is not original_enabled
assert bcde_feat.value is not original_enabled
await babycry.set_enabled(original_enabled)
await dev.update()
assert babycry.enabled is original_enabled
assert bcde_feat.value is original_enabled
await bcde_feat.set_value(not original_enabled)
await dev.update()
assert babycry.enabled is not original_enabled
assert bcde_feat.value is not original_enabled
finally:
await babycry.set_enabled(original_enabled)

View File

@ -0,0 +1,43 @@
"""Tests for smartcam motion detection module."""
from __future__ import annotations
from kasa import Device
from kasa.smartcam.smartcammodule import SmartCamModule
from ...device_fixtures import parametrize
motiondetection = parametrize(
"has motion detection", component_filter="detection", protocol_filter={"SMARTCAM"}
)
@motiondetection
async def test_motiondetection(dev: Device):
"""Test device motion detection."""
motion = dev.modules.get(SmartCamModule.SmartCamMotionDetection)
assert motion
mde_feat = dev.features.get("motion_detection")
assert mde_feat
original_enabled = motion.enabled
try:
await motion.set_enabled(not original_enabled)
await dev.update()
assert motion.enabled is not original_enabled
assert mde_feat.value is not original_enabled
await motion.set_enabled(original_enabled)
await dev.update()
assert motion.enabled is original_enabled
assert mde_feat.value is original_enabled
await mde_feat.set_value(not original_enabled)
await dev.update()
assert motion.enabled is not original_enabled
assert mde_feat.value is not original_enabled
finally:
await motion.set_enabled(original_enabled)

View File

@ -0,0 +1,45 @@
"""Tests for smartcam person detection module."""
from __future__ import annotations
from kasa import Device
from kasa.smartcam.smartcammodule import SmartCamModule
from ...device_fixtures import parametrize
persondetection = parametrize(
"has person detection",
component_filter="personDetection",
protocol_filter={"SMARTCAM"},
)
@persondetection
async def test_persondetection(dev: Device):
"""Test device person detection."""
person = dev.modules.get(SmartCamModule.SmartCamPersonDetection)
assert person
pde_feat = dev.features.get("person_detection")
assert pde_feat
original_enabled = person.enabled
try:
await person.set_enabled(not original_enabled)
await dev.update()
assert person.enabled is not original_enabled
assert pde_feat.value is not original_enabled
await person.set_enabled(original_enabled)
await dev.update()
assert person.enabled is original_enabled
assert pde_feat.value is original_enabled
await pde_feat.set_value(not original_enabled)
await dev.update()
assert person.enabled is not original_enabled
assert pde_feat.value is not original_enabled
finally:
await person.set_enabled(original_enabled)

View File

@ -0,0 +1,45 @@
"""Tests for smartcam tamper detection module."""
from __future__ import annotations
from kasa import Device
from kasa.smartcam.smartcammodule import SmartCamModule
from ...device_fixtures import parametrize
tamperdetection = parametrize(
"has tamper detection",
component_filter="tamperDetection",
protocol_filter={"SMARTCAM"},
)
@tamperdetection
async def test_tamperdetection(dev: Device):
"""Test device tamper detection."""
tamper = dev.modules.get(SmartCamModule.SmartCamTamperDetection)
assert tamper
tde_feat = dev.features.get("tamper_detection")
assert tde_feat
original_enabled = tamper.enabled
try:
await tamper.set_enabled(not original_enabled)
await dev.update()
assert tamper.enabled is not original_enabled
assert tde_feat.value is not original_enabled
await tamper.set_enabled(original_enabled)
await dev.update()
assert tamper.enabled is original_enabled
assert tde_feat.value is original_enabled
await tde_feat.set_value(not original_enabled)
await dev.update()
assert tamper.enabled is not original_enabled
assert tde_feat.value is not original_enabled
finally:
await tamper.set_enabled(original_enabled)