Home Assistant Unofficial Reference 2024.12.1
vacuum.py
Go to the documentation of this file.
1 """Support for Tuya Vacuums."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from tuya_sharing import CustomerDevice, Manager
8 
10  STATE_CLEANING,
11  STATE_DOCKED,
12  STATE_RETURNING,
13  StateVacuumEntity,
14  VacuumEntityFeature,
15 )
16 from homeassistant.const import STATE_IDLE, STATE_PAUSED
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.helpers.dispatcher import async_dispatcher_connect
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 
21 from . import TuyaConfigEntry
22 from .const import TUYA_DISCOVERY_NEW, DPCode, DPType
23 from .entity import EnumTypeData, IntegerTypeData, TuyaEntity
24 
25 TUYA_MODE_RETURN_HOME = "chargego"
26 TUYA_STATUS_TO_HA = {
27  "charge_done": STATE_DOCKED,
28  "chargecompleted": STATE_DOCKED,
29  "chargego": STATE_DOCKED,
30  "charging": STATE_DOCKED,
31  "cleaning": STATE_CLEANING,
32  "docking": STATE_RETURNING,
33  "goto_charge": STATE_RETURNING,
34  "goto_pos": STATE_CLEANING,
35  "mop_clean": STATE_CLEANING,
36  "part_clean": STATE_CLEANING,
37  "paused": STATE_PAUSED,
38  "pick_zone_clean": STATE_CLEANING,
39  "pos_arrived": STATE_CLEANING,
40  "pos_unarrive": STATE_CLEANING,
41  "random": STATE_CLEANING,
42  "sleep": STATE_IDLE,
43  "smart_clean": STATE_CLEANING,
44  "smart": STATE_CLEANING,
45  "spot_clean": STATE_CLEANING,
46  "standby": STATE_IDLE,
47  "wall_clean": STATE_CLEANING,
48  "wall_follow": STATE_CLEANING,
49  "zone_clean": STATE_CLEANING,
50 }
51 
52 
54  hass: HomeAssistant, entry: TuyaConfigEntry, async_add_entities: AddEntitiesCallback
55 ) -> None:
56  """Set up Tuya vacuum dynamically through Tuya discovery."""
57  hass_data = entry.runtime_data
58 
59  @callback
60  def async_discover_device(device_ids: list[str]) -> None:
61  """Discover and add a discovered Tuya vacuum."""
62  entities: list[TuyaVacuumEntity] = []
63  for device_id in device_ids:
64  device = hass_data.manager.device_map[device_id]
65  if device.category == "sd":
66  entities.append(TuyaVacuumEntity(device, hass_data.manager))
67  async_add_entities(entities)
68 
69  async_discover_device([*hass_data.manager.device_map])
70 
71  entry.async_on_unload(
72  async_dispatcher_connect(hass, TUYA_DISCOVERY_NEW, async_discover_device)
73  )
74 
75 
77  """Tuya Vacuum Device."""
78 
79  _fan_speed: EnumTypeData | None = None
80  _battery_level: IntegerTypeData | None = None
81  _attr_name = None
82 
83  def __init__(self, device: CustomerDevice, device_manager: Manager) -> None:
84  """Init Tuya vacuum."""
85  super().__init__(device, device_manager)
86 
87  self._attr_fan_speed_list_attr_fan_speed_list = []
88 
89  self._attr_supported_features_attr_supported_features = (
90  VacuumEntityFeature.SEND_COMMAND | VacuumEntityFeature.STATE
91  )
92  if self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(DPCode.PAUSE, prefer_function=True):
93  self._attr_supported_features_attr_supported_features |= VacuumEntityFeature.PAUSE
94 
95  if (
96  self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(DPCode.SWITCH_CHARGE, prefer_function=True)
97  or (
98  enum_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
99  DPCode.MODE, dptype=DPType.ENUM, prefer_function=True
100  )
101  )
102  and TUYA_MODE_RETURN_HOME in enum_type.range
103  ):
104  self._attr_supported_features_attr_supported_features |= VacuumEntityFeature.RETURN_HOME
105 
106  if self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(DPCode.SEEK, prefer_function=True):
107  self._attr_supported_features_attr_supported_features |= VacuumEntityFeature.LOCATE
108 
109  if self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(DPCode.POWER_GO, prefer_function=True):
110  self._attr_supported_features_attr_supported_features |= (
111  VacuumEntityFeature.STOP | VacuumEntityFeature.START
112  )
113 
114  if enum_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(
115  DPCode.SUCTION, dptype=DPType.ENUM, prefer_function=True
116  ):
117  self._fan_speed_fan_speed = enum_type
118  self._attr_fan_speed_list_attr_fan_speed_list = enum_type.range
119  self._attr_supported_features_attr_supported_features |= VacuumEntityFeature.FAN_SPEED
120 
121  if int_type := self.find_dpcodefind_dpcodefind_dpcodefind_dpcodefind_dpcode(DPCode.ELECTRICITY_LEFT, dptype=DPType.INTEGER):
122  self._attr_supported_features_attr_supported_features |= VacuumEntityFeature.BATTERY
123  self._battery_level_battery_level = int_type
124 
125  @property
126  def battery_level(self) -> int | None:
127  """Return Tuya device state."""
128  if self._battery_level_battery_level is None or not (
129  status := self.devicedevice.status.get(DPCode.ELECTRICITY_LEFT)
130  ):
131  return None
132  return round(self._battery_level_battery_level.scale_value(status))
133 
134  @property
135  def fan_speed(self) -> str | None:
136  """Return the fan speed of the vacuum cleaner."""
137  return self.devicedevice.status.get(DPCode.SUCTION)
138 
139  @property
140  def state(self) -> str | None:
141  """Return Tuya vacuum device state."""
142  if self.devicedevice.status.get(DPCode.PAUSE) and not (
143  self.devicedevice.status.get(DPCode.STATUS)
144  ):
145  return STATE_PAUSED
146  if not (status := self.devicedevice.status.get(DPCode.STATUS)):
147  return None
148  return TUYA_STATUS_TO_HA.get(status)
149 
150  def start(self, **kwargs: Any) -> None:
151  """Start the device."""
152  self._send_command_send_command([{"code": DPCode.POWER_GO, "value": True}])
153 
154  def stop(self, **kwargs: Any) -> None:
155  """Stop the device."""
156  self._send_command_send_command([{"code": DPCode.POWER_GO, "value": False}])
157 
158  def pause(self, **kwargs: Any) -> None:
159  """Pause the device."""
160  self._send_command_send_command([{"code": DPCode.POWER_GO, "value": False}])
161 
162  def return_to_base(self, **kwargs: Any) -> None:
163  """Return device to dock."""
164  self._send_command_send_command(
165  [
166  {"code": DPCode.SWITCH_CHARGE, "value": True},
167  {"code": DPCode.MODE, "value": TUYA_MODE_RETURN_HOME},
168  ]
169  )
170 
171  def locate(self, **kwargs: Any) -> None:
172  """Locate the device."""
173  self._send_command_send_command([{"code": DPCode.SEEK, "value": True}])
174 
175  def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
176  """Set fan speed."""
177  self._send_command_send_command([{"code": DPCode.SUCTION, "value": fan_speed}])
178 
180  self,
181  command: str,
182  params: dict[str, Any] | list[Any] | None = None,
183  **kwargs: Any,
184  ) -> None:
185  """Send raw command."""
186  if not params:
187  raise ValueError("Params cannot be omitted for Tuya vacuum commands")
188  if not isinstance(params, list):
189  raise TypeError("Params must be a list for Tuya vacuum commands")
190  self._send_command_send_command([{"code": command, "value": params[0]}])
None _send_command(self, list[dict[str, Any]] commands)
Definition: entity.py:295
DPCode|EnumTypeData|IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, DPType|None dptype=None)
Definition: entity.py:206
IntegerTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.INTEGER] dptype)
Definition: entity.py:190
DPCode|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False)
Definition: entity.py:198
EnumTypeData|None find_dpcode(self, str|DPCode|tuple[DPCode,...]|None dpcodes, *bool prefer_function=False, Literal[DPType.ENUM] dptype)
Definition: entity.py:181
None __init__(self, CustomerDevice device, Manager device_manager)
Definition: vacuum.py:83
None send_command(self, str command, dict[str, Any]|list[Any]|None params=None, **Any kwargs)
Definition: vacuum.py:184
None set_fan_speed(self, str fan_speed, **Any kwargs)
Definition: vacuum.py:175
ElkSystem|None async_discover_device(HomeAssistant hass, str host)
Definition: discovery.py:78
None async_setup_entry(HomeAssistant hass, TuyaConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: vacuum.py:55
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103