Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Roborock sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 import datetime
8 
9 from roborock.code_mappings import DyadError, RoborockDyadStateCode, ZeoError, ZeoState
10 from roborock.containers import (
11  RoborockDockErrorCode,
12  RoborockDockTypeCode,
13  RoborockErrorCode,
14  RoborockStateCode,
15 )
16 from roborock.roborock_message import (
17  RoborockDataProtocol,
18  RoborockDyadDataProtocol,
19  RoborockZeoProtocol,
20 )
21 from roborock.roborock_typing import DeviceProp
22 
24  SensorDeviceClass,
25  SensorEntity,
26  SensorEntityDescription,
27 )
28 from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfArea, UnitOfTime
29 from homeassistant.core import HomeAssistant
30 from homeassistant.helpers.entity_platform import AddEntitiesCallback
31 from homeassistant.helpers.typing import StateType
32 
33 from . import RoborockConfigEntry
34 from .coordinator import RoborockDataUpdateCoordinator, RoborockDataUpdateCoordinatorA01
35 from .entity import RoborockCoordinatedEntityA01, RoborockCoordinatedEntityV1
36 
37 
38 @dataclass(frozen=True, kw_only=True)
40  """A class that describes Roborock sensors."""
41 
42  value_fn: Callable[[DeviceProp], StateType | datetime.datetime]
43 
44  protocol_listener: RoborockDataProtocol | None = None
45 
46 
47 @dataclass(frozen=True, kw_only=True)
49  """A class that describes Roborock sensors."""
50 
51  data_protocol: RoborockDyadDataProtocol | RoborockZeoProtocol
52 
53 
54 def _dock_error_value_fn(properties: DeviceProp) -> str | None:
55  if (
56  status := properties.status.dock_error_status
57  ) is not None and properties.status.dock_type != RoborockDockTypeCode.no_dock:
58  return status.name
59 
60  return None
61 
62 
63 SENSOR_DESCRIPTIONS = [
65  native_unit_of_measurement=UnitOfTime.SECONDS,
66  key="main_brush_time_left",
67  device_class=SensorDeviceClass.DURATION,
68  translation_key="main_brush_time_left",
69  value_fn=lambda data: data.consumable.main_brush_time_left,
70  entity_category=EntityCategory.DIAGNOSTIC,
71  protocol_listener=RoborockDataProtocol.MAIN_BRUSH_WORK_TIME,
72  ),
74  native_unit_of_measurement=UnitOfTime.SECONDS,
75  key="side_brush_time_left",
76  device_class=SensorDeviceClass.DURATION,
77  translation_key="side_brush_time_left",
78  value_fn=lambda data: data.consumable.side_brush_time_left,
79  entity_category=EntityCategory.DIAGNOSTIC,
80  protocol_listener=RoborockDataProtocol.SIDE_BRUSH_WORK_TIME,
81  ),
83  native_unit_of_measurement=UnitOfTime.SECONDS,
84  key="filter_time_left",
85  device_class=SensorDeviceClass.DURATION,
86  translation_key="filter_time_left",
87  value_fn=lambda data: data.consumable.filter_time_left,
88  entity_category=EntityCategory.DIAGNOSTIC,
89  protocol_listener=RoborockDataProtocol.FILTER_WORK_TIME,
90  ),
92  native_unit_of_measurement=UnitOfTime.SECONDS,
93  key="sensor_time_left",
94  device_class=SensorDeviceClass.DURATION,
95  translation_key="sensor_time_left",
96  value_fn=lambda data: data.consumable.sensor_time_left,
97  entity_category=EntityCategory.DIAGNOSTIC,
98  ),
100  native_unit_of_measurement=UnitOfTime.SECONDS,
101  key="cleaning_time",
102  translation_key="cleaning_time",
103  device_class=SensorDeviceClass.DURATION,
104  value_fn=lambda data: data.status.clean_time,
105  entity_category=EntityCategory.DIAGNOSTIC,
106  ),
108  native_unit_of_measurement=UnitOfTime.SECONDS,
109  key="total_cleaning_time",
110  translation_key="total_cleaning_time",
111  device_class=SensorDeviceClass.DURATION,
112  value_fn=lambda data: data.clean_summary.clean_time,
113  entity_category=EntityCategory.DIAGNOSTIC,
114  ),
116  key="status",
117  device_class=SensorDeviceClass.ENUM,
118  translation_key="status",
119  value_fn=lambda data: data.status.state_name,
120  entity_category=EntityCategory.DIAGNOSTIC,
121  options=RoborockStateCode.keys(),
122  protocol_listener=RoborockDataProtocol.STATE,
123  ),
125  key="cleaning_area",
126  translation_key="cleaning_area",
127  value_fn=lambda data: data.status.square_meter_clean_area,
128  entity_category=EntityCategory.DIAGNOSTIC,
129  native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
130  ),
132  key="total_cleaning_area",
133  translation_key="total_cleaning_area",
134  value_fn=lambda data: data.clean_summary.square_meter_clean_area,
135  entity_category=EntityCategory.DIAGNOSTIC,
136  native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
137  ),
139  key="vacuum_error",
140  translation_key="vacuum_error",
141  device_class=SensorDeviceClass.ENUM,
142  value_fn=lambda data: data.status.error_code_name,
143  entity_category=EntityCategory.DIAGNOSTIC,
144  options=RoborockErrorCode.keys(),
145  protocol_listener=RoborockDataProtocol.ERROR_CODE,
146  ),
148  key="battery",
149  value_fn=lambda data: data.status.battery,
150  entity_category=EntityCategory.DIAGNOSTIC,
151  native_unit_of_measurement=PERCENTAGE,
152  device_class=SensorDeviceClass.BATTERY,
153  protocol_listener=RoborockDataProtocol.BATTERY,
154  ),
156  key="last_clean_start",
157  translation_key="last_clean_start",
158  value_fn=lambda data: data.last_clean_record.begin_datetime
159  if data.last_clean_record is not None
160  else None,
161  entity_category=EntityCategory.DIAGNOSTIC,
162  device_class=SensorDeviceClass.TIMESTAMP,
163  ),
165  key="last_clean_end",
166  translation_key="last_clean_end",
167  value_fn=lambda data: data.last_clean_record.end_datetime
168  if data.last_clean_record is not None
169  else None,
170  entity_category=EntityCategory.DIAGNOSTIC,
171  device_class=SensorDeviceClass.TIMESTAMP,
172  ),
173  # Only available on some newer models
175  key="clean_percent",
176  translation_key="clean_percent",
177  value_fn=lambda data: data.status.clean_percent,
178  entity_category=EntityCategory.DIAGNOSTIC,
179  native_unit_of_measurement=PERCENTAGE,
180  ),
181  # Only available with more than just the basic dock
183  key="dock_error",
184  translation_key="dock_error",
185  value_fn=_dock_error_value_fn,
186  entity_category=EntityCategory.DIAGNOSTIC,
187  device_class=SensorDeviceClass.ENUM,
188  options=RoborockDockErrorCode.keys(),
189  ),
191  key="mop_clean_remaining",
192  native_unit_of_measurement=UnitOfTime.SECONDS,
193  device_class=SensorDeviceClass.DURATION,
194  value_fn=lambda data: data.status.rdt,
195  translation_key="mop_drying_remaining_time",
196  entity_category=EntityCategory.DIAGNOSTIC,
197  ),
198 ]
199 
200 
201 A01_SENSOR_DESCRIPTIONS: list[RoborockSensorDescriptionA01] = [
203  key="status",
204  data_protocol=RoborockDyadDataProtocol.STATUS,
205  translation_key="a01_status",
206  entity_category=EntityCategory.DIAGNOSTIC,
207  device_class=SensorDeviceClass.ENUM,
208  options=RoborockDyadStateCode.keys(),
209  ),
211  key="battery",
212  data_protocol=RoborockDyadDataProtocol.POWER,
213  entity_category=EntityCategory.DIAGNOSTIC,
214  native_unit_of_measurement=PERCENTAGE,
215  device_class=SensorDeviceClass.BATTERY,
216  ),
218  key="filter_time_left",
219  data_protocol=RoborockDyadDataProtocol.MESH_LEFT,
220  native_unit_of_measurement=UnitOfTime.SECONDS,
221  device_class=SensorDeviceClass.DURATION,
222  translation_key="filter_time_left",
223  entity_category=EntityCategory.DIAGNOSTIC,
224  ),
226  key="brush_remaining",
227  data_protocol=RoborockDyadDataProtocol.BRUSH_LEFT,
228  native_unit_of_measurement=UnitOfTime.SECONDS,
229  device_class=SensorDeviceClass.DURATION,
230  translation_key="brush_remaining",
231  entity_category=EntityCategory.DIAGNOSTIC,
232  ),
234  key="error",
235  data_protocol=RoborockDyadDataProtocol.ERROR,
236  device_class=SensorDeviceClass.ENUM,
237  translation_key="a01_error",
238  entity_category=EntityCategory.DIAGNOSTIC,
239  options=DyadError.keys(),
240  ),
242  key="total_cleaning_time",
243  native_unit_of_measurement=UnitOfTime.MINUTES,
244  data_protocol=RoborockDyadDataProtocol.TOTAL_RUN_TIME,
245  device_class=SensorDeviceClass.DURATION,
246  translation_key="total_cleaning_time",
247  entity_category=EntityCategory.DIAGNOSTIC,
248  ),
250  key="state",
251  data_protocol=RoborockZeoProtocol.STATE,
252  translation_key="zeo_state",
253  entity_category=EntityCategory.DIAGNOSTIC,
254  device_class=SensorDeviceClass.ENUM,
255  options=ZeoState.keys(),
256  ),
258  key="countdown",
259  native_unit_of_measurement=UnitOfTime.MINUTES,
260  data_protocol=RoborockZeoProtocol.COUNTDOWN,
261  device_class=SensorDeviceClass.DURATION,
262  translation_key="countdown",
263  entity_category=EntityCategory.DIAGNOSTIC,
264  ),
266  key="washing_left",
267  native_unit_of_measurement=UnitOfTime.MINUTES,
268  data_protocol=RoborockZeoProtocol.WASHING_LEFT,
269  device_class=SensorDeviceClass.DURATION,
270  translation_key="washing_left",
271  entity_category=EntityCategory.DIAGNOSTIC,
272  ),
274  key="error",
275  data_protocol=RoborockZeoProtocol.ERROR,
276  device_class=SensorDeviceClass.ENUM,
277  translation_key="zeo_error",
278  entity_category=EntityCategory.DIAGNOSTIC,
279  options=ZeoError.keys(),
280  ),
281 ]
282 
283 
285  hass: HomeAssistant,
286  config_entry: RoborockConfigEntry,
287  async_add_entities: AddEntitiesCallback,
288 ) -> None:
289  """Set up the Roborock vacuum sensors."""
290  coordinators = config_entry.runtime_data
293  coordinator,
294  description,
295  )
296  for coordinator in coordinators.v1
297  for description in SENSOR_DESCRIPTIONS
298  if description.value_fn(coordinator.roborock_device_info.props) is not None
299  )
302  coordinator,
303  description,
304  )
305  for coordinator in coordinators.a01
306  for description in A01_SENSOR_DESCRIPTIONS
307  if description.data_protocol in coordinator.data
308  )
309 
310 
312  """Representation of a Roborock sensor."""
313 
314  entity_description: RoborockSensorDescription
315 
316  def __init__(
317  self,
318  coordinator: RoborockDataUpdateCoordinator,
319  description: RoborockSensorDescription,
320  ) -> None:
321  """Initialize the entity."""
322  self.entity_descriptionentity_description = description
323  super().__init__(
324  f"{description.key}_{coordinator.duid_slug}",
325  coordinator,
326  description.protocol_listener,
327  )
328 
329  @property
330  def native_value(self) -> StateType | datetime.datetime:
331  """Return the value reported by the sensor."""
332  return self.entity_descriptionentity_description.value_fn(
333  self.coordinator.roborock_device_info.props
334  )
335 
336 
338  """Representation of a A01 Roborock sensor."""
339 
340  entity_description: RoborockSensorDescriptionA01
341 
342  def __init__(
343  self,
344  coordinator: RoborockDataUpdateCoordinatorA01,
345  description: RoborockSensorDescriptionA01,
346  ) -> None:
347  """Initialize the entity."""
348  self.entity_descriptionentity_description = description
349  super().__init__(f"{description.key}_{coordinator.duid_slug}", coordinator)
350 
351  @property
352  def native_value(self) -> StateType:
353  """Return the value reported by the sensor."""
354  return self.coordinator.data[self.entity_descriptionentity_description.data_protocol]
None __init__(self, RoborockDataUpdateCoordinatorA01 coordinator, RoborockSensorDescriptionA01 description)
Definition: sensor.py:346
StateType|datetime.datetime native_value(self)
Definition: sensor.py:330
None __init__(self, RoborockDataUpdateCoordinator coordinator, RoborockSensorDescription description)
Definition: sensor.py:320
None async_setup_entry(HomeAssistant hass, RoborockConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:288
str|None _dock_error_value_fn(DeviceProp properties)
Definition: sensor.py:54