Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for vacuum cleaner robots (botvacs)."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 from enum import IntFlag
7 from functools import partial
8 import logging
9 from typing import Any
10 
11 from propcache import cached_property
12 import voluptuous as vol
13 
14 from homeassistant.config_entries import ConfigEntry
15 from homeassistant.const import ( # noqa: F401 # STATE_PAUSED/IDLE are API
16  ATTR_BATTERY_LEVEL,
17  ATTR_COMMAND,
18  SERVICE_TOGGLE,
19  SERVICE_TURN_OFF,
20  SERVICE_TURN_ON,
21  STATE_IDLE,
22  STATE_ON,
23  STATE_PAUSED,
24 )
25 from homeassistant.core import HomeAssistant
26 from homeassistant.helpers import config_validation as cv
28  DeprecatedConstantEnum,
29  all_with_deprecated_constants,
30  check_if_deprecated_constant,
31  dir_with_deprecated_constants,
32 )
33 from homeassistant.helpers.entity import Entity, EntityDescription
34 from homeassistant.helpers.entity_component import EntityComponent
35 from homeassistant.helpers.icon import icon_for_battery_level
36 from homeassistant.helpers.typing import ConfigType
37 from homeassistant.loader import bind_hass
38 from homeassistant.util.hass_dict import HassKey
39 
40 from .const import DOMAIN, STATE_CLEANING, STATE_DOCKED, STATE_ERROR, STATE_RETURNING
41 
42 _LOGGER = logging.getLogger(__name__)
43 
44 DATA_COMPONENT: HassKey[EntityComponent[StateVacuumEntity]] = HassKey(DOMAIN)
45 ENTITY_ID_FORMAT = DOMAIN + ".{}"
46 PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA
47 PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE
48 SCAN_INTERVAL = timedelta(seconds=20)
49 
50 ATTR_BATTERY_ICON = "battery_icon"
51 ATTR_CLEANED_AREA = "cleaned_area"
52 ATTR_FAN_SPEED = "fan_speed"
53 ATTR_FAN_SPEED_LIST = "fan_speed_list"
54 ATTR_PARAMS = "params"
55 ATTR_STATUS = "status"
56 
57 SERVICE_CLEAN_SPOT = "clean_spot"
58 SERVICE_LOCATE = "locate"
59 SERVICE_RETURN_TO_BASE = "return_to_base"
60 SERVICE_SEND_COMMAND = "send_command"
61 SERVICE_SET_FAN_SPEED = "set_fan_speed"
62 SERVICE_START_PAUSE = "start_pause"
63 SERVICE_START = "start"
64 SERVICE_PAUSE = "pause"
65 SERVICE_STOP = "stop"
66 
67 
68 STATES = [STATE_CLEANING, STATE_DOCKED, STATE_RETURNING, STATE_ERROR]
69 
70 DEFAULT_NAME = "Vacuum cleaner robot"
71 
72 
73 class VacuumEntityFeature(IntFlag):
74  """Supported features of the vacuum entity."""
75 
76  TURN_ON = 1 # Deprecated, not supported by StateVacuumEntity
77  TURN_OFF = 2 # Deprecated, not supported by StateVacuumEntity
78  PAUSE = 4
79  STOP = 8
80  RETURN_HOME = 16
81  FAN_SPEED = 32
82  BATTERY = 64
83  STATUS = 128 # Deprecated, not supported by StateVacuumEntity
84  SEND_COMMAND = 256
85  LOCATE = 512
86  CLEAN_SPOT = 1024
87  MAP = 2048
88  STATE = 4096 # Must be set by vacuum platforms derived from StateVacuumEntity
89  START = 8192
90 
91 
92 # These SUPPORT_* constants are deprecated as of Home Assistant 2022.5.
93 # Please use the VacuumEntityFeature enum instead.
94 _DEPRECATED_SUPPORT_TURN_ON = DeprecatedConstantEnum(
95  VacuumEntityFeature.TURN_ON, "2025.10"
96 )
97 _DEPRECATED_SUPPORT_TURN_OFF = DeprecatedConstantEnum(
98  VacuumEntityFeature.TURN_OFF, "2025.10"
99 )
100 _DEPRECATED_SUPPORT_PAUSE = DeprecatedConstantEnum(VacuumEntityFeature.PAUSE, "2025.10")
101 _DEPRECATED_SUPPORT_STOP = DeprecatedConstantEnum(VacuumEntityFeature.STOP, "2025.10")
102 _DEPRECATED_SUPPORT_RETURN_HOME = DeprecatedConstantEnum(
103  VacuumEntityFeature.RETURN_HOME, "2025.10"
104 )
105 _DEPRECATED_SUPPORT_FAN_SPEED = DeprecatedConstantEnum(
106  VacuumEntityFeature.FAN_SPEED, "2025.10"
107 )
108 _DEPRECATED_SUPPORT_BATTERY = DeprecatedConstantEnum(
109  VacuumEntityFeature.BATTERY, "2025.10"
110 )
111 _DEPRECATED_SUPPORT_STATUS = DeprecatedConstantEnum(
112  VacuumEntityFeature.STATUS, "2025.10"
113 )
114 _DEPRECATED_SUPPORT_SEND_COMMAND = DeprecatedConstantEnum(
115  VacuumEntityFeature.SEND_COMMAND, "2025.10"
116 )
117 _DEPRECATED_SUPPORT_LOCATE = DeprecatedConstantEnum(
118  VacuumEntityFeature.LOCATE, "2025.10"
119 )
120 _DEPRECATED_SUPPORT_CLEAN_SPOT = DeprecatedConstantEnum(
121  VacuumEntityFeature.CLEAN_SPOT, "2025.10"
122 )
123 _DEPRECATED_SUPPORT_MAP = DeprecatedConstantEnum(VacuumEntityFeature.MAP, "2025.10")
124 _DEPRECATED_SUPPORT_STATE = DeprecatedConstantEnum(VacuumEntityFeature.STATE, "2025.10")
125 _DEPRECATED_SUPPORT_START = DeprecatedConstantEnum(VacuumEntityFeature.START, "2025.10")
126 
127 # mypy: disallow-any-generics
128 
129 
130 @bind_hass
131 def is_on(hass: HomeAssistant, entity_id: str) -> bool:
132  """Return if the vacuum is on based on the statemachine."""
133  return hass.states.is_state(entity_id, STATE_ON)
134 
135 
136 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
137  """Set up the vacuum component."""
138  component = hass.data[DATA_COMPONENT] = EntityComponent[StateVacuumEntity](
139  _LOGGER, DOMAIN, hass, SCAN_INTERVAL
140  )
141 
142  await component.async_setup(config)
143 
144  component.async_register_entity_service(
145  SERVICE_START,
146  None,
147  "async_start",
148  [VacuumEntityFeature.START],
149  )
150  component.async_register_entity_service(
151  SERVICE_PAUSE,
152  None,
153  "async_pause",
154  [VacuumEntityFeature.PAUSE],
155  )
156  component.async_register_entity_service(
157  SERVICE_RETURN_TO_BASE,
158  None,
159  "async_return_to_base",
160  [VacuumEntityFeature.RETURN_HOME],
161  )
162  component.async_register_entity_service(
163  SERVICE_CLEAN_SPOT,
164  None,
165  "async_clean_spot",
166  [VacuumEntityFeature.CLEAN_SPOT],
167  )
168  component.async_register_entity_service(
169  SERVICE_LOCATE,
170  None,
171  "async_locate",
172  [VacuumEntityFeature.LOCATE],
173  )
174  component.async_register_entity_service(
175  SERVICE_STOP,
176  None,
177  "async_stop",
178  [VacuumEntityFeature.STOP],
179  )
180  component.async_register_entity_service(
181  SERVICE_SET_FAN_SPEED,
182  {vol.Required(ATTR_FAN_SPEED): cv.string},
183  "async_set_fan_speed",
184  [VacuumEntityFeature.FAN_SPEED],
185  )
186  component.async_register_entity_service(
187  SERVICE_SEND_COMMAND,
188  {
189  vol.Required(ATTR_COMMAND): cv.string,
190  vol.Optional(ATTR_PARAMS): vol.Any(dict, cv.ensure_list),
191  },
192  "async_send_command",
193  [VacuumEntityFeature.SEND_COMMAND],
194  )
195 
196  return True
197 
198 
199 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
200  """Set up a config entry."""
201  return await hass.data[DATA_COMPONENT].async_setup_entry(entry)
202 
203 
204 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
205  """Unload a config entry."""
206  return await hass.data[DATA_COMPONENT].async_unload_entry(entry)
207 
208 
209 class StateVacuumEntityDescription(EntityDescription, frozen_or_thawed=True):
210  """A class that describes vacuum entities."""
211 
212 
213 STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_ = {
214  "supported_features",
215  "battery_level",
216  "battery_icon",
217  "fan_speed",
218  "fan_speed_list",
219  "state",
220 }
221 
222 
224  Entity, cached_properties=STATE_VACUUM_CACHED_PROPERTIES_WITH_ATTR_
225 ):
226  """Representation of a vacuum cleaner robot that supports states."""
227 
228  entity_description: StateVacuumEntityDescription
229 
230  _entity_component_unrecorded_attributes = frozenset({ATTR_FAN_SPEED_LIST})
231 
232  _attr_battery_icon: str
233  _attr_battery_level: int | None = None
234  _attr_fan_speed: str | None = None
235  _attr_fan_speed_list: list[str]
236  _attr_state: str | None = None
237  _attr_supported_features: VacuumEntityFeature = VacuumEntityFeature(0)
238 
239  @cached_property
240  def battery_level(self) -> int | None:
241  """Return the battery level of the vacuum cleaner."""
242  return self._attr_battery_level
243 
244  @property
245  def battery_icon(self) -> str:
246  """Return the battery icon for the vacuum cleaner."""
247  charging = bool(self.statestatestatestate == STATE_DOCKED)
248 
249  return icon_for_battery_level(
250  battery_level=self.battery_levelbattery_level, charging=charging
251  )
252 
253  @property
254  def capability_attributes(self) -> dict[str, Any] | None:
255  """Return capability attributes."""
256  if VacuumEntityFeature.FAN_SPEED in self.supported_features_compatsupported_features_compat:
257  return {ATTR_FAN_SPEED_LIST: self.fan_speed_listfan_speed_list}
258  return None
259 
260  @cached_property
261  def fan_speed(self) -> str | None:
262  """Return the fan speed of the vacuum cleaner."""
263  return self._attr_fan_speed
264 
265  @cached_property
266  def fan_speed_list(self) -> list[str]:
267  """Get the list of available fan speed steps of the vacuum cleaner."""
268  return self._attr_fan_speed_list
269 
270  @property
271  def state_attributes(self) -> dict[str, Any]:
272  """Return the state attributes of the vacuum cleaner."""
273  data: dict[str, Any] = {}
274  supported_features = self.supported_features_compatsupported_features_compat
275 
276  if VacuumEntityFeature.BATTERY in supported_features:
277  data[ATTR_BATTERY_LEVEL] = self.battery_levelbattery_level
278  data[ATTR_BATTERY_ICON] = self.battery_iconbattery_icon
279 
280  if VacuumEntityFeature.FAN_SPEED in supported_features:
281  data[ATTR_FAN_SPEED] = self.fan_speedfan_speed
282 
283  return data
284 
285  @cached_property
286  def state(self) -> str | None:
287  """Return the state of the vacuum cleaner."""
288  return self._attr_state
289 
290  @cached_property
291  def supported_features(self) -> VacuumEntityFeature:
292  """Flag vacuum cleaner features that are supported."""
293  return self._attr_supported_features
294 
295  @property
296  def supported_features_compat(self) -> VacuumEntityFeature:
297  """Return the supported features as VacuumEntityFeature.
298 
299  Remove this compatibility shim in 2025.1 or later.
300  """
301  features = self.supported_featuressupported_featuressupported_features
302  if type(features) is int: # noqa: E721
303  new_features = VacuumEntityFeature(features)
304  self._report_deprecated_supported_features_values_report_deprecated_supported_features_values(new_features)
305  return new_features
306  return features
307 
308  def stop(self, **kwargs: Any) -> None:
309  """Stop the vacuum cleaner."""
310  raise NotImplementedError
311 
312  async def async_stop(self, **kwargs: Any) -> None:
313  """Stop the vacuum cleaner.
314 
315  This method must be run in the event loop.
316  """
317  await self.hasshass.async_add_executor_job(partial(self.stopstop, **kwargs))
318 
319  def return_to_base(self, **kwargs: Any) -> None:
320  """Set the vacuum cleaner to return to the dock."""
321  raise NotImplementedError
322 
323  async def async_return_to_base(self, **kwargs: Any) -> None:
324  """Set the vacuum cleaner to return to the dock.
325 
326  This method must be run in the event loop.
327  """
328  await self.hasshass.async_add_executor_job(partial(self.return_to_basereturn_to_base, **kwargs))
329 
330  def clean_spot(self, **kwargs: Any) -> None:
331  """Perform a spot clean-up."""
332  raise NotImplementedError
333 
334  async def async_clean_spot(self, **kwargs: Any) -> None:
335  """Perform a spot clean-up.
336 
337  This method must be run in the event loop.
338  """
339  await self.hasshass.async_add_executor_job(partial(self.clean_spotclean_spot, **kwargs))
340 
341  def locate(self, **kwargs: Any) -> None:
342  """Locate the vacuum cleaner."""
343  raise NotImplementedError
344 
345  async def async_locate(self, **kwargs: Any) -> None:
346  """Locate the vacuum cleaner.
347 
348  This method must be run in the event loop.
349  """
350  await self.hasshass.async_add_executor_job(partial(self.locatelocate, **kwargs))
351 
352  def set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
353  """Set fan speed."""
354  raise NotImplementedError
355 
356  async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
357  """Set fan speed.
358 
359  This method must be run in the event loop.
360  """
361  await self.hasshass.async_add_executor_job(
362  partial(self.set_fan_speedset_fan_speed, fan_speed, **kwargs)
363  )
364 
366  self,
367  command: str,
368  params: dict[str, Any] | list[Any] | None = None,
369  **kwargs: Any,
370  ) -> None:
371  """Send a command to a vacuum cleaner."""
372  raise NotImplementedError
373 
375  self,
376  command: str,
377  params: dict[str, Any] | list[Any] | None = None,
378  **kwargs: Any,
379  ) -> None:
380  """Send a command to a vacuum cleaner.
381 
382  This method must be run in the event loop.
383  """
384  await self.hasshass.async_add_executor_job(
385  partial(self.send_commandsend_command, command, params=params, **kwargs)
386  )
387 
388  def start(self) -> None:
389  """Start or resume the cleaning task."""
390  raise NotImplementedError
391 
392  async def async_start(self) -> None:
393  """Start or resume the cleaning task.
394 
395  This method must be run in the event loop.
396  """
397  await self.hasshass.async_add_executor_job(self.startstart)
398 
399  def pause(self) -> None:
400  """Pause the cleaning task."""
401  raise NotImplementedError
402 
403  async def async_pause(self) -> None:
404  """Pause the cleaning task.
405 
406  This method must be run in the event loop.
407  """
408  await self.hasshass.async_add_executor_job(self.pausepause)
409 
410 
411 # As we import deprecated constants from the const module, we need to add these two functions
412 # otherwise this module will be logged for using deprecated constants and not the custom component
413 # These can be removed if no deprecated constant are in this module anymore
414 __getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
415 __dir__ = partial(
416  dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
417 )
418 __all__ = all_with_deprecated_constants(globals())
None async_set_fan_speed(self, str fan_speed, **Any kwargs)
Definition: __init__.py:356
None set_fan_speed(self, str fan_speed, **Any kwargs)
Definition: __init__.py:352
None send_command(self, str command, dict[str, Any]|list[Any]|None params=None, **Any kwargs)
Definition: __init__.py:370
dict[str, Any]|None capability_attributes(self)
Definition: __init__.py:254
None async_return_to_base(self, **Any kwargs)
Definition: __init__.py:323
None async_send_command(self, str command, dict[str, Any]|list[Any]|None params=None, **Any kwargs)
Definition: __init__.py:379
VacuumEntityFeature supported_features(self)
Definition: __init__.py:291
VacuumEntityFeature supported_features_compat(self)
Definition: __init__.py:296
None _report_deprecated_supported_features_values(self, IntFlag replacement)
Definition: entity.py:1645
int|None supported_features(self)
Definition: entity.py:861
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:199
bool is_on(HomeAssistant hass, str entity_id)
Definition: __init__.py:131
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:204
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:136
list[str] all_with_deprecated_constants(dict[str, Any] module_globals)
Definition: deprecation.py:356
str icon_for_battery_level(int|None battery_level=None, bool charging=False)
Definition: icon.py:169