Skip to content

Commit aa31df0

Browse files
authored
Migrate Tuya camera to use wrapper class (home-assistant#156542)
1 parent 13fbeb6 commit aa31df0

File tree

3 files changed

+89
-9
lines changed

3 files changed

+89
-9
lines changed

homeassistant/components/tuya/camera.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from . import TuyaConfigEntry
1414
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
1515
from .entity import TuyaEntity
16+
from .models import DPCodeBooleanWrapper
1617

1718
CAMERAS: tuple[DeviceCategory, ...] = (
1819
DeviceCategory.DGHSXJ,
@@ -35,7 +36,18 @@ def async_discover_device(device_ids: list[str]) -> None:
3536
for device_id in device_ids:
3637
device = manager.device_map[device_id]
3738
if device.category in CAMERAS:
38-
entities.append(TuyaCameraEntity(device, manager))
39+
entities.append(
40+
TuyaCameraEntity(
41+
device,
42+
manager,
43+
motion_detection_switch=DPCodeBooleanWrapper.find_dpcode(
44+
device, DPCode.MOTION_SWITCH, prefer_function=True
45+
),
46+
recording_status=DPCodeBooleanWrapper.find_dpcode(
47+
device, DPCode.RECORD_SWITCH
48+
),
49+
)
50+
)
3951

4052
async_add_entities(entities)
4153

@@ -57,21 +69,30 @@ def __init__(
5769
self,
5870
device: CustomerDevice,
5971
device_manager: Manager,
72+
*,
73+
motion_detection_switch: DPCodeBooleanWrapper | None = None,
74+
recording_status: DPCodeBooleanWrapper | None = None,
6075
) -> None:
6176
"""Init Tuya Camera."""
6277
super().__init__(device, device_manager)
6378
CameraEntity.__init__(self)
6479
self._attr_model = device.product_name
80+
self._motion_detection_switch = motion_detection_switch
81+
self._recording_status = recording_status
6582

6683
@property
6784
def is_recording(self) -> bool:
6885
"""Return true if the device is recording."""
69-
return self.device.status.get(DPCode.RECORD_SWITCH, False)
86+
if (status := self._read_wrapper(self._recording_status)) is not None:
87+
return status
88+
return False
7089

7190
@property
7291
def motion_detection_enabled(self) -> bool:
7392
"""Return the camera motion detection status."""
74-
return self.device.status.get(DPCode.MOTION_SWITCH, False)
93+
if (status := self._read_wrapper(self._motion_detection_switch)) is not None:
94+
return status
95+
return False
7596

7697
async def stream_source(self) -> str | None:
7798
"""Return the source of the stream."""
@@ -95,10 +116,10 @@ async def async_camera_image(
95116
height=height,
96117
)
97118

98-
def enable_motion_detection(self) -> None:
119+
async def async_enable_motion_detection(self) -> None:
99120
"""Enable motion detection in the camera."""
100-
self._send_command([{"code": DPCode.MOTION_SWITCH, "value": True}])
121+
await self._async_send_dpcode_update(self._motion_detection_switch, True)
101122

102-
def disable_motion_detection(self) -> None:
123+
async def async_disable_motion_detection(self) -> None:
103124
"""Disable motion detection in camera."""
104-
self._send_command([{"code": DPCode.MOTION_SWITCH, "value": False}])
125+
await self._async_send_dpcode_update(self._motion_detection_switch, False)

homeassistant/components/tuya/entity.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,18 @@ def _send_command(self, commands: list[dict[str, Any]]) -> None:
6666
LOGGER.debug("Sending commands for device %s: %s", self.device.id, commands)
6767
self.device_manager.send_commands(self.device.id, commands)
6868

69+
def _read_wrapper(self, dpcode_wrapper: DPCodeWrapper | None) -> Any | None:
70+
"""Read the wrapper device status."""
71+
if dpcode_wrapper is None:
72+
return None
73+
return dpcode_wrapper.read_device_status(self.device)
74+
6975
async def _async_send_dpcode_update(
70-
self, dpcode_wrapper: DPCodeWrapper, value: Any
76+
self, dpcode_wrapper: DPCodeWrapper | None, value: Any
7177
) -> None:
7278
"""Send command to the device."""
79+
if dpcode_wrapper is None:
80+
return
7381
await self.hass.async_add_executor_job(
7482
self._send_command,
7583
[dpcode_wrapper.get_update_command(self.device, value)],

tests/components/tuya/test_camera.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
from __future__ import annotations
44

5+
from typing import Any
56
from unittest.mock import patch
67

78
import pytest
89
from syrupy.assertion import SnapshotAssertion
910
from tuya_sharing import CustomerDevice, Manager
1011

11-
from homeassistant.const import Platform
12+
from homeassistant.components.camera import (
13+
DOMAIN as CAMERA_DOMAIN,
14+
SERVICE_DISABLE_MOTION,
15+
SERVICE_ENABLE_MOTION,
16+
)
17+
from homeassistant.const import ATTR_ENTITY_ID, Platform
1218
from homeassistant.core import HomeAssistant
1319
from homeassistant.helpers import entity_registry as er
1420

@@ -46,3 +52,48 @@ async def test_platform_setup_and_discovery(
4652
snapshot,
4753
mock_config_entry.entry_id,
4854
)
55+
56+
57+
@patch("homeassistant.components.tuya.PLATFORMS", [Platform.CAMERA])
58+
@pytest.mark.parametrize(
59+
"mock_device_code",
60+
["sp_rudejjigkywujjvs"],
61+
)
62+
@pytest.mark.parametrize(
63+
("service", "expected_command"),
64+
[
65+
(
66+
SERVICE_DISABLE_MOTION,
67+
{"code": "motion_switch", "value": False},
68+
),
69+
(
70+
SERVICE_ENABLE_MOTION,
71+
{"code": "motion_switch", "value": True},
72+
),
73+
],
74+
)
75+
async def test_motion_detection(
76+
hass: HomeAssistant,
77+
mock_manager: Manager,
78+
mock_config_entry: MockConfigEntry,
79+
mock_device: CustomerDevice,
80+
service: str,
81+
expected_command: dict[str, Any],
82+
) -> None:
83+
"""Test turning off a switch."""
84+
entity_id = "camera.burocam"
85+
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
86+
87+
state = hass.states.get(entity_id)
88+
assert state is not None, f"{entity_id} does not exist"
89+
await hass.services.async_call(
90+
CAMERA_DOMAIN,
91+
service,
92+
{
93+
ATTR_ENTITY_ID: entity_id,
94+
},
95+
blocking=True,
96+
)
97+
mock_manager.send_commands.assert_called_once_with(
98+
mock_device.id, [expected_command]
99+
)

0 commit comments

Comments
 (0)