Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """The Aprilaire sensor component."""
2 
3 from __future__ import annotations
4 
5 from dataclasses import dataclass
6 from typing import cast
7 
8 from pyaprilaire.const import Attribute
9 
11  SensorDeviceClass,
12  SensorEntity,
13  SensorEntityDescription,
14  SensorStateClass,
15 )
16 from homeassistant.const import PERCENTAGE, UnitOfTemperature
17 from homeassistant.core import HomeAssistant
18 from homeassistant.helpers.entity_platform import AddEntitiesCallback
19 from homeassistant.helpers.typing import StateType
20 
21 from .coordinator import AprilaireConfigEntry, AprilaireCoordinator
22 from .entity import BaseAprilaireEntity
23 
24 DEHUMIDIFICATION_STATUS_MAP: dict[StateType, str] = {
25  0: "idle",
26  1: "idle",
27  2: "on",
28  3: "on",
29  4: "off",
30 }
31 
32 HUMIDIFICATION_STATUS_MAP: dict[StateType, str] = {
33  0: "idle",
34  1: "idle",
35  2: "on",
36  3: "off",
37 }
38 
39 VENTILATION_STATUS_MAP: dict[StateType, str] = {
40  0: "idle",
41  1: "idle",
42  2: "on",
43  3: "idle",
44  4: "idle",
45  5: "idle",
46  6: "off",
47 }
48 
49 AIR_CLEANING_STATUS_MAP: dict[StateType, str] = {
50  0: "idle",
51  1: "idle",
52  2: "on",
53  3: "off",
54 }
55 
56 FAN_STATUS_MAP: dict[StateType, str] = {0: "off", 1: "on"}
57 
58 
60  entity_class: type[BaseAprilaireSensor],
61  coordinator: AprilaireCoordinator,
62  unique_id: str,
63  descriptions: tuple[AprilaireSensorDescription, ...],
64 ) -> list[BaseAprilaireSensor]:
65  """Get the entities for a list of sensor descriptions."""
66 
67  entities = (
68  entity_class(coordinator, description, unique_id)
69  for description in descriptions
70  )
71 
72  return [entity for entity in entities if entity.exists]
73 
74 
76  hass: HomeAssistant,
77  config_entry: AprilaireConfigEntry,
78  async_add_entities: AddEntitiesCallback,
79 ) -> None:
80  """Set up Aprilaire sensor devices."""
81 
82  coordinator = config_entry.runtime_data
83 
84  assert config_entry.unique_id is not None
85 
86  entities = (
88  AprilaireHumiditySensor,
89  coordinator,
90  config_entry.unique_id,
91  HUMIDITY_SENSORS,
92  )
93  + get_entities(
94  AprilaireTemperatureSensor,
95  coordinator,
96  config_entry.unique_id,
97  TEMPERATURE_SENSORS,
98  )
99  + get_entities(
100  AprilaireStatusSensor, coordinator, config_entry.unique_id, STATUS_SENSORS
101  )
102  )
103 
104  async_add_entities(entities)
105 
106 
107 @dataclass(frozen=True, kw_only=True)
109  """Class describing Aprilaire sensor entities."""
110 
111  status_key: str | None
112  value_key: str
113 
114 
115 @dataclass(frozen=True, kw_only=True)
117  """Class describing Aprilaire status sensor entities."""
118 
119  status_map: dict[StateType, str]
120 
121 
122 HUMIDITY_SENSORS: tuple[AprilaireSensorDescription, ...] = (
124  key="indoor_humidity_controlling_sensor",
125  translation_key="indoor_humidity_controlling_sensor",
126  device_class=SensorDeviceClass.HUMIDITY,
127  state_class=SensorStateClass.MEASUREMENT,
128  native_unit_of_measurement=PERCENTAGE,
129  status_key=Attribute.INDOOR_HUMIDITY_CONTROLLING_SENSOR_STATUS,
130  value_key=Attribute.INDOOR_HUMIDITY_CONTROLLING_SENSOR_VALUE,
131  ),
133  key="outdoor_humidity_controlling_sensor",
134  translation_key="outdoor_humidity_controlling_sensor",
135  device_class=SensorDeviceClass.HUMIDITY,
136  state_class=SensorStateClass.MEASUREMENT,
137  native_unit_of_measurement=PERCENTAGE,
138  status_key=Attribute.OUTDOOR_HUMIDITY_CONTROLLING_SENSOR_STATUS,
139  value_key=Attribute.OUTDOOR_HUMIDITY_CONTROLLING_SENSOR_VALUE,
140  ),
141 )
142 
143 TEMPERATURE_SENSORS: tuple[AprilaireSensorDescription, ...] = (
145  key="indoor_temperature_controlling_sensor",
146  translation_key="indoor_temperature_controlling_sensor",
147  device_class=SensorDeviceClass.TEMPERATURE,
148  state_class=SensorStateClass.MEASUREMENT,
149  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
150  status_key=Attribute.INDOOR_TEMPERATURE_CONTROLLING_SENSOR_STATUS,
151  value_key=Attribute.INDOOR_TEMPERATURE_CONTROLLING_SENSOR_VALUE,
152  ),
154  key="outdoor_temperature_controlling_sensor",
155  translation_key="outdoor_temperature_controlling_sensor",
156  device_class=SensorDeviceClass.TEMPERATURE,
157  state_class=SensorStateClass.MEASUREMENT,
158  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
159  status_key=Attribute.OUTDOOR_TEMPERATURE_CONTROLLING_SENSOR_STATUS,
160  value_key=Attribute.OUTDOOR_TEMPERATURE_CONTROLLING_SENSOR_VALUE,
161  ),
162 )
163 
164 STATUS_SENSORS: tuple[AprilaireSensorDescription, ...] = (
166  key="dehumidification_status",
167  translation_key="dehumidification_status",
168  device_class=SensorDeviceClass.ENUM,
169  status_key=Attribute.DEHUMIDIFICATION_AVAILABLE,
170  value_key=Attribute.DEHUMIDIFICATION_STATUS,
171  status_map=DEHUMIDIFICATION_STATUS_MAP,
172  options=list(set(DEHUMIDIFICATION_STATUS_MAP.values())),
173  ),
175  key="humidification_status",
176  translation_key="humidification_status",
177  device_class=SensorDeviceClass.ENUM,
178  status_key=Attribute.HUMIDIFICATION_AVAILABLE,
179  value_key=Attribute.HUMIDIFICATION_STATUS,
180  status_map=HUMIDIFICATION_STATUS_MAP,
181  options=list(set(HUMIDIFICATION_STATUS_MAP.values())),
182  ),
184  key="ventilation_status",
185  translation_key="ventilation_status",
186  device_class=SensorDeviceClass.ENUM,
187  status_key=Attribute.VENTILATION_AVAILABLE,
188  value_key=Attribute.VENTILATION_STATUS,
189  status_map=VENTILATION_STATUS_MAP,
190  options=list(set(VENTILATION_STATUS_MAP.values())),
191  ),
193  key="air_cleaning_status",
194  translation_key="air_cleaning_status",
195  device_class=SensorDeviceClass.ENUM,
196  status_key=Attribute.AIR_CLEANING_AVAILABLE,
197  value_key=Attribute.AIR_CLEANING_STATUS,
198  status_map=AIR_CLEANING_STATUS_MAP,
199  options=list(set(AIR_CLEANING_STATUS_MAP.values())),
200  ),
202  key="fan_status",
203  translation_key="fan_status",
204  device_class=SensorDeviceClass.ENUM,
205  status_key=None,
206  value_key=Attribute.FAN_STATUS,
207  status_map=FAN_STATUS_MAP,
208  options=list(set(FAN_STATUS_MAP.values())),
209  ),
210 )
211 
212 
214  """Base sensor entity for Aprilaire."""
215 
216  entity_description: AprilaireSensorDescription
217  status_sensor_available_value: int | None = None
218  status_sensor_exists_values: list[int]
219 
220  def __init__(
221  self,
222  coordinator: AprilaireCoordinator,
223  description: AprilaireSensorDescription,
224  unique_id: str,
225  ) -> None:
226  """Initialize a sensor for an Aprilaire device."""
227 
228  self.entity_descriptionentity_description = description
229 
230  super().__init__(coordinator, unique_id)
231 
232  @property
233  def exists(self) -> bool:
234  """Return True if the sensor exists."""
235 
236  if self.entity_descriptionentity_description.status_key is None:
237  return True
238 
239  return (
240  self.coordinator.data.get(self.entity_descriptionentity_description.status_key)
241  in self.status_sensor_exists_values
242  )
243 
244  @property
245  def available(self) -> bool:
246  """Return True if the sensor is available."""
247 
248  if (
249  self.entity_descriptionentity_description.status_key is None
250  or self.status_sensor_available_value is None
251  ):
252  return True
253 
254  if not super().available:
255  return False
256 
257  return (
258  self.coordinator.data.get(self.entity_descriptionentity_description.status_key)
259  == self.status_sensor_available_value
260  )
261 
262  @property
263  def native_value(self) -> StateType:
264  """Return the value reported by the sensor."""
265 
266  # Valid cast as pyaprilaire only provides str | int | float
267  return cast(
268  StateType, self.coordinator.data.get(self.entity_descriptionentity_description.value_key)
269  )
270 
271 
273  """Humidity sensor entity for Aprilaire."""
274 
275  status_sensor_available_value = 0
276  status_sensor_exists_values = [0, 1, 2]
277 
278 
280  """Temperature sensor entity for Aprilaire."""
281 
282  status_sensor_available_value = 0
283  status_sensor_exists_values = [0, 1, 2]
284 
285  @property
286  def suggested_display_precision(self) -> int | None:
287  """Return the suggested number of decimal digits for display."""
288  if self.unit_of_measurementunit_of_measurementunit_of_measurementunit_of_measurementunit_of_measurement == UnitOfTemperature.CELSIUS:
289  return 1
290 
291  return 0
292 
293 
295  """Status sensor entity for Aprilaire."""
296 
297  status_sensor_exists_values = [1, 2]
298  entity_description: AprilaireStatusSensorDescription
299 
300  @property
301  def native_value(self) -> StateType:
302  """Return the value reported by the sensor mapped to the status option."""
303 
304  raw_value = super().native_value
305 
306  return self.entity_descriptionentity_description.status_map.get(raw_value)
None __init__(self, AprilaireCoordinator coordinator, AprilaireSensorDescription description, str unique_id)
Definition: sensor.py:225
str|None unit_of_measurement(self)
Definition: entity.py:815
None async_setup_entry(HomeAssistant hass, AprilaireConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:79
list[BaseAprilaireSensor] get_entities(type[BaseAprilaireSensor] entity_class, AprilaireCoordinator coordinator, str unique_id, tuple[AprilaireSensorDescription,...] descriptions)
Definition: sensor.py:64