Skip to content

Commit 796b421

Browse files
authored
Migrate Tuya vacuum to use wrapper class (home-assistant#156569)
1 parent 0c03e8d commit 796b421

File tree

2 files changed

+123
-43
lines changed

2 files changed

+123
-43
lines changed

homeassistant/components/tuya/vacuum.py

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1717

1818
from . import TuyaConfigEntry
19-
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode, DPType
19+
from .const import TUYA_DISCOVERY_NEW, DeviceCategory, DPCode
2020
from .entity import TuyaEntity
21-
from .models import EnumTypeData, find_dpcode
21+
from .models import DPCodeBooleanWrapper, DPCodeEnumWrapper
2222
from .util import get_dpcode
2323

2424
TUYA_MODE_RETURN_HOME = "chargego"
@@ -64,7 +64,27 @@ def async_discover_device(device_ids: list[str]) -> None:
6464
for device_id in device_ids:
6565
device = manager.device_map[device_id]
6666
if device.category == DeviceCategory.SD:
67-
entities.append(TuyaVacuumEntity(device, manager))
67+
entities.append(
68+
TuyaVacuumEntity(
69+
device,
70+
manager,
71+
charge_wrapper=DPCodeBooleanWrapper.find_dpcode(
72+
device, DPCode.SWITCH_CHARGE, prefer_function=True
73+
),
74+
fan_speed_wrapper=DPCodeEnumWrapper.find_dpcode(
75+
device, DPCode.SUCTION, prefer_function=True
76+
),
77+
locate_wrapper=DPCodeBooleanWrapper.find_dpcode(
78+
device, DPCode.SEEK, prefer_function=True
79+
),
80+
mode_wrapper=DPCodeEnumWrapper.find_dpcode(
81+
device, DPCode.MODE, prefer_function=True
82+
),
83+
switch_wrapper=DPCodeBooleanWrapper.find_dpcode(
84+
device, DPCode.POWER_GO, prefer_function=True
85+
),
86+
)
87+
)
6888
async_add_entities(entities)
6989

7090
async_discover_device([*manager.device_map])
@@ -77,51 +97,56 @@ def async_discover_device(device_ids: list[str]) -> None:
7797
class TuyaVacuumEntity(TuyaEntity, StateVacuumEntity):
7898
"""Tuya Vacuum Device."""
7999

80-
_fan_speed: EnumTypeData | None = None
81100
_attr_name = None
82101

83-
def __init__(self, device: CustomerDevice, device_manager: Manager) -> None:
102+
def __init__(
103+
self,
104+
device: CustomerDevice,
105+
device_manager: Manager,
106+
*,
107+
charge_wrapper: DPCodeBooleanWrapper | None,
108+
fan_speed_wrapper: DPCodeEnumWrapper | None,
109+
locate_wrapper: DPCodeBooleanWrapper | None,
110+
mode_wrapper: DPCodeEnumWrapper | None,
111+
switch_wrapper: DPCodeBooleanWrapper | None,
112+
) -> None:
84113
"""Init Tuya vacuum."""
85114
super().__init__(device, device_manager)
115+
self._charge_wrapper = charge_wrapper
116+
self._fan_speed_wrapper = fan_speed_wrapper
117+
self._locate_wrapper = locate_wrapper
118+
self._mode_wrapper = mode_wrapper
119+
self._switch_wrapper = switch_wrapper
86120

87121
self._attr_fan_speed_list = []
88-
89122
self._attr_supported_features = (
90123
VacuumEntityFeature.SEND_COMMAND | VacuumEntityFeature.STATE
91124
)
92125
if get_dpcode(self.device, DPCode.PAUSE):
93126
self._attr_supported_features |= VacuumEntityFeature.PAUSE
94127

95-
self._return_home_use_switch_charge = False
96-
if get_dpcode(self.device, DPCode.SWITCH_CHARGE):
97-
self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME
98-
self._return_home_use_switch_charge = True
99-
elif (
100-
enum_type := find_dpcode(
101-
self.device, DPCode.MODE, dptype=DPType.ENUM, prefer_function=True
102-
)
103-
) and TUYA_MODE_RETURN_HOME in enum_type.range:
128+
if charge_wrapper or (
129+
mode_wrapper
130+
and TUYA_MODE_RETURN_HOME in mode_wrapper.type_information.range
131+
):
104132
self._attr_supported_features |= VacuumEntityFeature.RETURN_HOME
105133

106-
if get_dpcode(self.device, DPCode.SEEK):
134+
if locate_wrapper:
107135
self._attr_supported_features |= VacuumEntityFeature.LOCATE
108136

109-
if get_dpcode(self.device, DPCode.POWER_GO):
137+
if switch_wrapper:
110138
self._attr_supported_features |= (
111139
VacuumEntityFeature.STOP | VacuumEntityFeature.START
112140
)
113141

114-
if enum_type := find_dpcode(
115-
self.device, DPCode.SUCTION, dptype=DPType.ENUM, prefer_function=True
116-
):
117-
self._fan_speed = enum_type
118-
self._attr_fan_speed_list = enum_type.range
142+
if fan_speed_wrapper:
143+
self._attr_fan_speed_list = fan_speed_wrapper.type_information.range
119144
self._attr_supported_features |= VacuumEntityFeature.FAN_SPEED
120145

121146
@property
122147
def fan_speed(self) -> str | None:
123148
"""Return the fan speed of the vacuum cleaner."""
124-
return self.device.status.get(DPCode.SUCTION)
149+
return self._read_wrapper(self._fan_speed_wrapper)
125150

126151
@property
127152
def activity(self) -> VacuumActivity | None:
@@ -134,32 +159,34 @@ def activity(self) -> VacuumActivity | None:
134159
return None
135160
return TUYA_STATUS_TO_HA.get(status)
136161

137-
def start(self, **kwargs: Any) -> None:
162+
async def async_start(self, **kwargs: Any) -> None:
138163
"""Start the device."""
139-
self._send_command([{"code": DPCode.POWER_GO, "value": True}])
164+
await self._async_send_dpcode_update(self._switch_wrapper, True)
140165

141-
def stop(self, **kwargs: Any) -> None:
166+
async def async_stop(self, **kwargs: Any) -> None:
142167
"""Stop the device."""
143-
self._send_command([{"code": DPCode.POWER_GO, "value": False}])
168+
await self._async_send_dpcode_update(self._switch_wrapper, False)
144169

145170
def pause(self, **kwargs: Any) -> None:
146171
"""Pause the device."""
147172
self._send_command([{"code": DPCode.POWER_GO, "value": False}])
148173

149-
def return_to_base(self, **kwargs: Any) -> None:
174+
async def async_return_to_base(self, **kwargs: Any) -> None:
150175
"""Return device to dock."""
151-
if self._return_home_use_switch_charge:
152-
self._send_command([{"code": DPCode.SWITCH_CHARGE, "value": True}])
176+
if self._charge_wrapper:
177+
await self._async_send_dpcode_update(self._charge_wrapper, True)
153178
else:
154-
self._send_command([{"code": DPCode.MODE, "value": TUYA_MODE_RETURN_HOME}])
179+
await self._async_send_dpcode_update(
180+
self._mode_wrapper, TUYA_MODE_RETURN_HOME
181+
)
155182

156-
def locate(self, **kwargs: Any) -> None:
183+
async def async_locate(self, **kwargs: Any) -> None:
157184
"""Locate the device."""
158-
self._send_command([{"code": DPCode.SEEK, "value": True}])
185+
await self._async_send_dpcode_update(self._locate_wrapper, True)
159186

160-
def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
187+
async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
161188
"""Set fan speed."""
162-
self._send_command([{"code": DPCode.SUCTION, "value": fan_speed}])
189+
await self._async_send_dpcode_update(self._fan_speed_wrapper, fan_speed)
163190

164191
def send_command(
165192
self,

tests/components/tuya/test_vacuum.py

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
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

1112
from homeassistant.components.vacuum import (
13+
ATTR_FAN_SPEED,
1214
DOMAIN as VACUUM_DOMAIN,
15+
SERVICE_LOCATE,
1316
SERVICE_RETURN_TO_BASE,
17+
SERVICE_SET_FAN_SPEED,
18+
SERVICE_START,
19+
SERVICE_STOP,
1420
)
1521
from homeassistant.const import ATTR_ENTITY_ID, Platform
1622
from homeassistant.core import HomeAssistant
@@ -37,30 +43,77 @@ async def test_platform_setup_and_discovery(
3743

3844

3945
@pytest.mark.parametrize(
40-
"mock_device_code",
41-
["sd_lr33znaodtyarrrz"],
46+
("mock_device_code", "entity_id", "service", "service_data", "expected_command"),
47+
[
48+
(
49+
"sd_i6hyjg3af7doaswm",
50+
"vacuum.hoover",
51+
SERVICE_RETURN_TO_BASE,
52+
{},
53+
{"code": "mode", "value": "chargego"},
54+
),
55+
(
56+
# Based on #141278
57+
"sd_lr33znaodtyarrrz",
58+
"vacuum.v20",
59+
SERVICE_RETURN_TO_BASE,
60+
{},
61+
{"code": "switch_charge", "value": True},
62+
),
63+
(
64+
"sd_lr33znaodtyarrrz",
65+
"vacuum.v20",
66+
SERVICE_SET_FAN_SPEED,
67+
{ATTR_FAN_SPEED: "gentle"},
68+
{"code": "suction", "value": "gentle"},
69+
),
70+
(
71+
"sd_i6hyjg3af7doaswm",
72+
"vacuum.hoover",
73+
SERVICE_LOCATE,
74+
{},
75+
{"code": "seek", "value": True},
76+
),
77+
(
78+
"sd_i6hyjg3af7doaswm",
79+
"vacuum.hoover",
80+
SERVICE_START,
81+
{},
82+
{"code": "power_go", "value": True},
83+
),
84+
(
85+
"sd_i6hyjg3af7doaswm",
86+
"vacuum.hoover",
87+
SERVICE_STOP,
88+
{},
89+
{"code": "power_go", "value": False},
90+
),
91+
],
4292
)
43-
async def test_return_home(
93+
async def test_action(
4494
hass: HomeAssistant,
4595
mock_manager: Manager,
4696
mock_config_entry: MockConfigEntry,
4797
mock_device: CustomerDevice,
98+
entity_id: str,
99+
service: str,
100+
service_data: dict[str, Any],
101+
expected_command: dict[str, Any],
48102
) -> None:
49-
"""Test return home service."""
50-
# Based on #141278
51-
entity_id = "vacuum.v20"
103+
"""Test service action."""
52104
await initialize_entry(hass, mock_manager, mock_config_entry, mock_device)
53105

54106
state = hass.states.get(entity_id)
55107
assert state is not None, f"{entity_id} does not exist"
56108
await hass.services.async_call(
57109
VACUUM_DOMAIN,
58-
SERVICE_RETURN_TO_BASE,
110+
service,
59111
{
60112
ATTR_ENTITY_ID: entity_id,
113+
**service_data,
61114
},
62115
blocking=True,
63116
)
64117
mock_manager.send_commands.assert_called_once_with(
65-
mock_device.id, [{"code": "switch_charge", "value": True}]
118+
mock_device.id, [expected_command]
66119
)

0 commit comments

Comments
 (0)