Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Litter-Robot sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime
8 from typing import Any, Generic, cast
9 
10 from pylitterbot import FeederRobot, LitterRobot, LitterRobot4, Robot
11 
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16  SensorStateClass,
17 )
18 from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfMass
19 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 
22 from . import LitterRobotConfigEntry
23 from .entity import LitterRobotEntity, _RobotT
24 
25 
26 def icon_for_gauge_level(gauge_level: int | None = None, offset: int = 0) -> str:
27  """Return a gauge icon valid identifier."""
28  if gauge_level is None or gauge_level <= 0 + offset:
29  return "mdi:gauge-empty"
30  if gauge_level > 70 + offset:
31  return "mdi:gauge-full"
32  if gauge_level > 30 + offset:
33  return "mdi:gauge"
34  return "mdi:gauge-low"
35 
36 
37 @dataclass(frozen=True)
39  """A class that describes robot sensor entities."""
40 
41  icon_fn: Callable[[Any], str | None] = lambda _: None
42  should_report: Callable[[_RobotT], bool] = lambda _: True
43 
44 
45 class LitterRobotSensorEntity(LitterRobotEntity[_RobotT], SensorEntity):
46  """Litter-Robot sensor entity."""
47 
48  entity_description: RobotSensorEntityDescription[_RobotT]
49 
50  @property
51  def native_value(self) -> float | datetime | str | None:
52  """Return the state."""
53  if self.entity_descriptionentity_description.should_report(self.robotrobot):
54  if isinstance(val := getattr(self.robotrobot, self.entity_descriptionentity_description.key), str):
55  return val.lower()
56  return cast(float | datetime | None, val)
57  return None
58 
59  @property
60  def icon(self) -> str | None:
61  """Return the icon to use in the frontend, if any."""
62  if (icon := self.entity_descriptionentity_description.icon_fn(self.statestatestate)) is not None:
63  return icon
64  return super().icon
65 
66 
67 ROBOT_SENSOR_MAP: dict[type[Robot], list[RobotSensorEntityDescription]] = {
68  LitterRobot: [ # type: ignore[type-abstract] # only used for isinstance check
69  RobotSensorEntityDescription[LitterRobot](
70  key="waste_drawer_level",
71  translation_key="waste_drawer",
72  native_unit_of_measurement=PERCENTAGE,
73  icon_fn=lambda state: icon_for_gauge_level(state, 10),
74  state_class=SensorStateClass.MEASUREMENT,
75  ),
76  RobotSensorEntityDescription[LitterRobot](
77  key="sleep_mode_start_time",
78  translation_key="sleep_mode_start_time",
79  device_class=SensorDeviceClass.TIMESTAMP,
80  should_report=lambda robot: robot.sleep_mode_enabled,
81  ),
82  RobotSensorEntityDescription[LitterRobot](
83  key="sleep_mode_end_time",
84  translation_key="sleep_mode_end_time",
85  device_class=SensorDeviceClass.TIMESTAMP,
86  should_report=lambda robot: robot.sleep_mode_enabled,
87  ),
88  RobotSensorEntityDescription[LitterRobot](
89  key="last_seen",
90  translation_key="last_seen",
91  device_class=SensorDeviceClass.TIMESTAMP,
92  entity_category=EntityCategory.DIAGNOSTIC,
93  ),
94  RobotSensorEntityDescription[LitterRobot](
95  key="status_code",
96  translation_key="status_code",
97  entity_category=EntityCategory.DIAGNOSTIC,
98  device_class=SensorDeviceClass.ENUM,
99  options=[
100  "br",
101  "ccc",
102  "ccp",
103  "cd",
104  "csf",
105  "csi",
106  "cst",
107  "df1",
108  "df2",
109  "dfs",
110  "dhf",
111  "dpf",
112  "ec",
113  "hpf",
114  "off",
115  "offline",
116  "otf",
117  "p",
118  "pd",
119  "pwrd",
120  "pwru",
121  "rdy",
122  "scf",
123  "sdf",
124  "spf",
125  ],
126  ),
127  ],
128  LitterRobot4: [
129  RobotSensorEntityDescription[LitterRobot4](
130  key="litter_level",
131  translation_key="litter_level",
132  native_unit_of_measurement=PERCENTAGE,
133  icon_fn=lambda state: icon_for_gauge_level(state, 10),
134  state_class=SensorStateClass.MEASUREMENT,
135  ),
136  RobotSensorEntityDescription[LitterRobot4](
137  key="pet_weight",
138  translation_key="pet_weight",
139  native_unit_of_measurement=UnitOfMass.POUNDS,
140  device_class=SensorDeviceClass.WEIGHT,
141  state_class=SensorStateClass.MEASUREMENT,
142  ),
143  ],
144  FeederRobot: [
145  RobotSensorEntityDescription[FeederRobot](
146  key="food_level",
147  translation_key="food_level",
148  native_unit_of_measurement=PERCENTAGE,
149  icon_fn=lambda state: icon_for_gauge_level(state, 10),
150  state_class=SensorStateClass.MEASUREMENT,
151  )
152  ],
153 }
154 
155 
157  hass: HomeAssistant,
158  entry: LitterRobotConfigEntry,
159  async_add_entities: AddEntitiesCallback,
160 ) -> None:
161  """Set up Litter-Robot sensors using config entry."""
162  hub = entry.runtime_data
163  entities = [
164  LitterRobotSensorEntity(robot=robot, hub=hub, description=description)
165  for robot in hub.account.robots
166  for robot_type, entity_descriptions in ROBOT_SENSOR_MAP.items()
167  if isinstance(robot, robot_type)
168  for description in entity_descriptions
169  ]
170  async_add_entities(entities)
str icon_for_gauge_level(int|None gauge_level=None, int offset=0)
Definition: sensor.py:26
None async_setup_entry(HomeAssistant hass, LitterRobotConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:160