8000 Add smartcam detection modules (#1389) · python-kasa/python-kasa@d890b0a · GitHub
[go: up one dir, main page]

Skip to content

Commit d890b0a

Browse files
authored
Add smartcam detection modules (#1389)
- Motion detection - Person detection - Tamper detection - Baby Cry Detection
1 parent b5f49a3 commit d890b0a

10 files changed

+386
-0
lines changed

kasa/smartcam/modules/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
"""Modules for SMARTCAM devices."""
22

33
from .alarm import Alarm
4+
from .babycrydetection import BabyCryDetection
45
from .camera import Camera
56
from .childdevice import ChildDevice
67
from .device import DeviceModule
78
from .homekit import HomeKit
89
from .led import Led
910
from .lensmask import LensMask
1011
from .matter import Matter
12+
from .motiondetection import MotionDetection
1113
from .pantilt import PanTilt
14+
from .persondetection import PersonDetection
15+
from .tamperdetection import TamperDetection
1216
from .time import Time
1317

1418
__all__ = [
1519
"Alarm",
20+
"BabyCryDetection",
1621
"Camera",
1722
"ChildDevice",
1823
"DeviceModule",
1924
"Led",
2025
"PanTilt",
26+
"PersonDetection",
2127
"Time",
2228
"HomeKit",
2329
"Matter",
30+
"MotionDetection",
2431
"LensMask",
32+
"TamperDetection",
2533
]
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Implementation of baby cry detection module."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from ...feature import Feature
8+
from ..smartcammodule import SmartCamModule
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
class BabyCryDetection(SmartCamModule):
14+
"""Implementation of baby cry detection module."""
15+
16+
REQUIRED_COMPONENT = "babyCryDetection"
17+
18+
QUERY_GETTER_NAME = "getBCDConfig"
19+
QUERY_MODULE_NAME = "sound_detection"
20+
QUERY_SECTION_NAMES = "bcd"
21+
22+
def _initialize_features(self) -> None:
23+
"""Initialize features after the initial update."""
24+
self._add_feature(
25+
Feature(
26+
self._device,
27+
id="baby_cry_detection",
28+
name="Baby cry detection",
29+
container=self,
30+
attribute_getter="enabled",
31+
attribute_setter="set_enabled",
32+
type=Feature.Type.Switch,
33+
category=Feature.Category.Primary,
34+
)
35+
)
36+
37+
@property
38+
def enabled(self) -> bool:
39+
"""Return the baby cry detection enabled state."""
40+
return self.data["bcd"]["enabled"] == "on"
41+
42+
async def set_enabled(self, enable: bool) -> dict:
43+
"""Set the baby cry detection enabled state."""
44+
params = {"enabled": F438 "on" if enable else "off"}
45+
return await self._device._query_setter_helper(
46+
"setBCDConfig", self.QUERY_MODULE_NAME, "bcd", params
47+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Implementation of motion detection module."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from ...feature import Feature
8+
from ..smartcammodule import SmartCamModule
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
class MotionDetection(SmartCamModule):
14+
"""Implementation of motion detection module."""
15+
16+
REQUIRED_COMPONENT = "detection"
17+
18+
QUERY_GETTER_NAME = "getDetectionConfig"
19+
QUERY_MODULE_NAME = "motion_detection"
20+
QUERY_SECTION_NAMES = "motion_det"
21+
22+
def _initialize_features(self) -> None:
23+
"""Initialize features after the initial update."""
24+
self._add_feature(
25+
Feature(
26+
self._device,
27+
id="motion_detection",
28+
name="Motion detection",
29+
container=self,
30+
attribute_getter="enabled",
31+
attribute_setter="set_enabled",
32+
type=Feature.Type.Switch,
33+
category=Feature.Category.Primary,
34+
)
35+
)
36+
37+
@property
38+
def enabled(self) -> bool:
39+
"""Return the motion detection enabled state."""
40+
return self.data["motion_det"]["enabled"] == "on"
41+
42+
async def set_enabled(self, enable: bool) -> dict:
43+
"""Set the motion detection enabled state."""
44+
params = {"enabled": "on" if enable else "off"}
45+
return await self._device._query_setter_helper(
46+
"setDetectionConfig", self.QUERY_MODULE_NAME, "motion_det", params
47+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Implementation of person detection module."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from ...feature import Feature
8+
from ..smartcammodule import SmartCamModule
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
class PersonDetection(SmartCamModule):
14+
"""Implementation of person detection module."""
15+
16+
REQUIRED_COMPONENT = "personDetection"
17+
18+
QUERY_GETTER_NAME = "getPersonDetectionConfig"
19+
QUERY_MODULE_NAME = "people_detection"
20+
QUERY_SECTION_NAMES = "detection"
21+
22+
def _initialize_features(self) -> None:
23+
"""Initialize features after the initial update."""
24+
self._add_feature(
25+
Feature(
26+
self._device,
27+
id="person_detection",
28+
name="Person detection",
29+
container=self,
30+
attribute_getter="enabled",
31+
attribute_setter="set_enabled",
32+
type=Feature.Type.Switch,
33+
category=Feature.Category.Primary,
34+
)
35+
)
36+
37+
@property
38+
def enabled(self) -> bool:
39+
"""Return the person detection enabled state."""
40+
return self.data["detection"]["enabled"] == "on"
41+
42+
async def set_enabled(self, enable: bool) -> dict:
43+
"""Set the person detection enabled state."""
44+
params = {"enabled": "on" if enable else "off"}
45+
return await self._device._query_setter_helper(
46+
"setPersonDetectionConfig", self.QUERY_MODULE_NAME, "detection", params
47+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Implementation of tamper detection module."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from ...feature import Feature
8+
from ..smartcammodule import SmartCamModule
9+
10+
_LOGGER = logging.getLogger(__name__)
11+
12+
13+
class TamperDetection(SmartCamModule):
14+
"""Implementation of tamper detection module."""
15+
16+
REQUIRED_COMPONENT = "tamperDetection"
17+
18+
QUERY_GETTER_NAME = "getTamperDetectionConfig"
19+
QUERY_MODULE_NAME = "tamper_detection"
20+
QUERY_SECTION_NAMES = "tamper_det"
21+
22+
def _initialize_features(self) -> None:
23+
"""Initialize features after the initial update."""
24+
self._add_feature(
25+
Feature(
26+
self._device,
27+
id="tamper_detection",
28+
name="Tamper detection",
29+
container=self,
30+
attribute_getter="enabled",
31+
attribute_setter="set_enabled",
32+
type=Feature.Type.Switch,
33+
category=Feature.Category.Primary,
34+
)
35+
)
36+
37+
@property
38+
def enabled(self) -> bool:
39+
"""Return the tamper detection enabled state."""
40+
return self.data["tamper_det"]["enabled"] == "on"
41+
42+
async def set_enabled(self, enable: bool) -> dict:
43+
"""Set the tamper detection enabled state."""
44+
params = {"enabled": "on" if enable else "off"}
45+
return await self._device._query_setter_helper(
46+
"setTamperDetectionConfig", self.QUERY_MODULE_NAME, "tamper_det", params
47+
)

kasa/smartcam/smartcammodule.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ class SmartCamModule(SmartModule):
2020
"""Base class for SMARTCAM modules."""
2121

2222
SmartCamAlarm: Final[ModuleName[modules.Alarm]] = ModuleName("SmartCamAlarm")
23+
SmartCamMotionDetection: Final[ModuleName[modules.MotionDetection]] = ModuleName(
24+
"MotionDetection"
25+
)
26+
SmartCamPersonDetection: Final[ModuleName[modules.PersonDetection]] = ModuleName(
27+
"PersonDetection"
28+
)
29+
SmartCamTamperDetection: Final[ModuleName[modules.TamperDetection]] = ModuleName(
30+
"TamperDetection"
31+
)
32+
SmartCamBabyCryDetection: Final[ModuleName[modules.BabyCryDetection]] = ModuleName(
33+
"BabyCryDetection&quo 10000 t;
34+
)
2335

2436
#: Module name to be queried
2537
QUERY_MODULE_NAME: str
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Tests for smartcam baby cry detection module."""
2+
3+
from __future__ import annotations
4+
5+
from kasa import Device
6+
from kasa.smartcam.smartcammodule import SmartCamModule
7+
8+
from ...device_fixtures import parametrize
9+
10+
babycrydetection = parametrize(
11+
"has babycry detection",
12+
component_filter="babyCryDetection",
13+
protocol_filter={"SMARTCAM"},
14+
)
15+
16+
17+
@babycrydetection
18+
async def test_babycrydetection(dev: Device):
19+
"""Test device babycry detection."""
20+
babycry = dev.modules.get(SmartCamModule.SmartCamBabyCryDetection)
21+
assert babycry
22+
23+
bcde_feat = dev.features.get("baby_cry_detection")
24+
assert bcde_feat
25+
26+
original_enabled = babycry.enabled
27+
28+
try:
29+
await babycry.set_enabled(not original_enabled)
30+
await dev.update()
31+
assert babycry.enabled is not original_enabled
32+
assert bcde_feat.value is not original_enabled
33+
34+
await babycry.set_enabled(original_enabled)
35+
await dev.update()
36+
assert babycry.enabled is original_enabled
37+
assert bcde_feat.value is original_enabled
38+
39+
await bcde_feat.set_value(not original_enabled)
40+
await dev.update()
41+
assert babycry.enabled is not original_enabled
42+
assert bcde_feat.value is not original_enabled
43+
44+
finally:
45+
await babycry.set_enabled(original_enabled)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""Tests for smartcam motion detection module."""
2+
3+
from __future__ import annotations
4+
5+
from kasa import Device
6+
from kasa.smartcam.smartcammodule import SmartCamModule
7+
8+
from ...device_fixtures import parametrize
9+
10+
motiondetection = parametrize(
11+
"has motion detection", component_filter="detection", protocol_filter={"SMARTCAM"}
12+
)
13+
14+
15+
@motiondetection
16+
async def test_motiondetection(dev: Device):
17+
"""Test device motion detection."""
18+
motion = dev.modules.get(SmartCamModule.SmartCamMotionDetection)
19+
assert motion
20+
21+
mde_feat = dev.features.get("motion_detection")
22+
assert mde_feat
23+
24+
original_enabled = motion.enabled
25+
26+
try:
27+
await motion.set_enabled(not original_enabled)
28+
await dev.update()
29+
assert motion.enabled is not original_enabled
30+
assert mde_feat.value is not original_enabled
31+
32+
await motion.set_enabled(original_enabled)
33+
await dev.update()
34+
assert motion.enabled is original_enabled
35+
assert mde_feat.value is original_enabled
36+
37+
await mde_feat.set_value(not original_enabled)
38+
await dev.update()
39+
assert motion.enabled is not original_enabled
40+
assert mde_feat.value is not original_enabled
41+
42+
finally:
43+
await motion.set_enabled(original_enabled)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Tests for smartcam person detection module."""
2+
3+
from __future__ import annotations
4+
5+
from kasa import Device
6+
from kasa.smartcam.smartcammodule import SmartCamModule
7+
8+
from ...device_fixtures import parametrize
9+
10+
persondetection = parametrize(
11+
"has person detection",
12+
component_filter="personDetection",
13+
protocol_filter={"SMARTCAM"},
14+
)
15+
16+
17+
@persondetection
18+
async def test_persondetection(dev: Device):
19+
"""Test device person detection."""
20+
person = dev.modules.get(SmartCamModule.SmartCamPersonDetection)
21+
assert person
22+
23+
pde_feat = dev.features.get("person_detection")
24+
assert pde_feat
25+
26+
original_enabled = person.enabled
27+
28+
try:
29+
await person.set_enabled(not original_enabled)
30+
await dev.update()
31+
assert person.enabled is not original_enabled
32+
assert pde_feat.value is not original_enabled
33+
34+
await person.set_enabled(original_enabled)
35+
await dev.update()
36+
assert person.enabled is original_enabled
37+
assert pde_feat.value is original_enabled
38+
39+
await pde_feat.set_value(not original_enabled)
40+
await dev.update()
41+
assert person.enabled is not original_enabled
42+
assert pde_feat.value is not original_enabled
43+
44+
finally:
45+
await person.set_enabled(original_enabled)

0 commit comments

Comments
 (0)
0