Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for RFXtrx sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import date, datetime
8 from decimal import Decimal
9 import logging
10 from typing import Any, cast
11 
12 from RFXtrx import ControlEvent, RFXtrxDevice, RFXtrxEvent, SensorEvent
13 
15  SensorDeviceClass,
16  SensorEntity,
17  SensorEntityDescription,
18  SensorStateClass,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.const import (
22  DEGREE,
23  PERCENTAGE,
24  SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
25  UV_INDEX,
26  EntityCategory,
27  UnitOfElectricCurrent,
28  UnitOfElectricPotential,
29  UnitOfEnergy,
30  UnitOfPower,
31  UnitOfPrecipitationDepth,
32  UnitOfPressure,
33  UnitOfSpeed,
34  UnitOfTemperature,
35  UnitOfVolumetricFlux,
36 )
37 from homeassistant.core import HomeAssistant, callback
38 from homeassistant.helpers.entity import Entity
39 from homeassistant.helpers.entity_platform import AddEntitiesCallback
40 from homeassistant.helpers.typing import StateType
41 
42 from . import DeviceTuple, async_setup_platform_entry, get_rfx_object
43 from .const import ATTR_EVENT
44 from .entity import RfxtrxEntity
45 
46 _LOGGER = logging.getLogger(__name__)
47 
48 
49 def _battery_convert(value: int | None) -> int | None:
50  """Battery is given as a value between 0 and 9."""
51  if value is None:
52  return None
53  return (value + 1) * 10
54 
55 
56 def _rssi_convert(value: int | None) -> str | None:
57  """Rssi is given as dBm value."""
58  if value is None:
59  return None
60  return f"{value*8-120}"
61 
62 
63 @dataclass(frozen=True)
65  """Description of sensor entities."""
66 
67  convert: Callable[[Any], StateType | date | datetime | Decimal] = lambda x: cast(
68  StateType, x
69  )
70 
71 
72 SENSOR_TYPES = (
74  key="Barometer",
75  device_class=SensorDeviceClass.PRESSURE,
76  state_class=SensorStateClass.MEASUREMENT,
77  native_unit_of_measurement=UnitOfPressure.HPA,
78  ),
80  key="Battery numeric",
81  device_class=SensorDeviceClass.BATTERY,
82  state_class=SensorStateClass.MEASUREMENT,
83  native_unit_of_measurement=PERCENTAGE,
84  convert=_battery_convert,
85  entity_category=EntityCategory.DIAGNOSTIC,
86  ),
88  key="Current",
89  device_class=SensorDeviceClass.CURRENT,
90  state_class=SensorStateClass.MEASUREMENT,
91  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
92  ),
94  key="Current Ch. 1",
95  translation_key="current_ch_1",
96  device_class=SensorDeviceClass.CURRENT,
97  state_class=SensorStateClass.MEASUREMENT,
98  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
99  ),
101  key="Current Ch. 2",
102  translation_key="current_ch_2",
103  device_class=SensorDeviceClass.CURRENT,
104  state_class=SensorStateClass.MEASUREMENT,
105  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
106  ),
108  key="Current Ch. 3",
109  translation_key="current_ch_3",
110  device_class=SensorDeviceClass.CURRENT,
111  state_class=SensorStateClass.MEASUREMENT,
112  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
113  ),
115  key="Energy usage",
116  translation_key="instantaneous_power",
117  device_class=SensorDeviceClass.POWER,
118  state_class=SensorStateClass.MEASUREMENT,
119  native_unit_of_measurement=UnitOfPower.WATT,
120  ),
122  key="Humidity",
123  device_class=SensorDeviceClass.HUMIDITY,
124  state_class=SensorStateClass.MEASUREMENT,
125  native_unit_of_measurement=PERCENTAGE,
126  ),
128  key="Rssi numeric",
129  device_class=SensorDeviceClass.SIGNAL_STRENGTH,
130  state_class=SensorStateClass.MEASUREMENT,
131  native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
132  convert=_rssi_convert,
133  entity_category=EntityCategory.DIAGNOSTIC,
134  ),
136  key="Temperature",
137  device_class=SensorDeviceClass.TEMPERATURE,
138  state_class=SensorStateClass.MEASUREMENT,
139  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
140  ),
142  key="Temperature2",
143  translation_key="temperature_2",
144  device_class=SensorDeviceClass.TEMPERATURE,
145  state_class=SensorStateClass.MEASUREMENT,
146  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
147  ),
149  key="Total usage",
150  translation_key="total_energy_usage",
151  device_class=SensorDeviceClass.ENERGY,
152  state_class=SensorStateClass.TOTAL_INCREASING,
153  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
154  ),
156  key="Voltage",
157  device_class=SensorDeviceClass.VOLTAGE,
158  state_class=SensorStateClass.MEASUREMENT,
159  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
160  ),
162  key="Wind direction",
163  translation_key="wind_direction",
164  state_class=SensorStateClass.MEASUREMENT,
165  native_unit_of_measurement=DEGREE,
166  ),
168  key="Rain rate",
169  state_class=SensorStateClass.MEASUREMENT,
170  native_unit_of_measurement=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
171  device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
172  ),
174  key="Sound",
175  translation_key="sound",
176  ),
178  key="Sensor Status",
179  translation_key="sensor_status",
180  ),
182  key="Count",
183  translation_key="count",
184  state_class=SensorStateClass.TOTAL_INCREASING,
185  native_unit_of_measurement="count",
186  ),
188  key="Counter value",
189  translation_key="counter_value",
190  state_class=SensorStateClass.TOTAL_INCREASING,
191  native_unit_of_measurement="count",
192  ),
194  key="Chill",
195  translation_key="chill",
196  device_class=SensorDeviceClass.TEMPERATURE,
197  state_class=SensorStateClass.MEASUREMENT,
198  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
199  ),
201  key="Wind average speed",
202  translation_key="wind_average_speed",
203  state_class=SensorStateClass.MEASUREMENT,
204  native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
205  device_class=SensorDeviceClass.WIND_SPEED,
206  ),
208  key="Wind gust",
209  translation_key="wind_gust",
210  state_class=SensorStateClass.MEASUREMENT,
211  native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
212  device_class=SensorDeviceClass.WIND_SPEED,
213  ),
215  key="Rain total",
216  state_class=SensorStateClass.MEASUREMENT,
217  native_unit_of_measurement=UnitOfPrecipitationDepth.MILLIMETERS,
218  device_class=SensorDeviceClass.PRECIPITATION,
219  ),
221  key="Forecast",
222  translation_key="forecast_status",
223  ),
225  key="Forecast numeric",
226  translation_key="forecast",
227  ),
229  key="Humidity status",
230  translation_key="humidity_status",
231  ),
233  key="UV",
234  translation_key="uv_index",
235  state_class=SensorStateClass.MEASUREMENT,
236  native_unit_of_measurement=UV_INDEX,
237  ),
238 )
239 
240 SENSOR_TYPES_DICT = {desc.key: desc for desc in SENSOR_TYPES}
241 
242 
244  hass: HomeAssistant,
245  config_entry: ConfigEntry,
246  async_add_entities: AddEntitiesCallback,
247 ) -> None:
248  """Set up config entry."""
249 
250  def _supported(event: RFXtrxEvent) -> bool:
251  return isinstance(event, (ControlEvent, SensorEvent))
252 
253  def _constructor(
254  event: RFXtrxEvent,
255  auto: RFXtrxEvent | None,
256  device_id: DeviceTuple,
257  entity_info: dict[str, Any],
258  ) -> list[Entity]:
259  return [
260  RfxtrxSensor(
261  event.device,
262  device_id,
263  SENSOR_TYPES_DICT[data_type],
264  event=event if auto else None,
265  )
266  for data_type in set(event.values) & set(SENSOR_TYPES_DICT)
267  ]
268 
270  hass, config_entry, async_add_entities, _supported, _constructor
271  )
272 
273 
274 # pylint: disable-next=hass-invalid-inheritance # needs fixing
276  """Representation of a RFXtrx sensor.
277 
278  Since all repeated events have meaning, these types of sensors
279  need to have force update enabled.
280  """
281 
282  _attr_force_update = True
283  entity_description: RfxtrxSensorEntityDescription
284 
285  def __init__(
286  self,
287  device: RFXtrxDevice,
288  device_id: DeviceTuple,
289  entity_description: RfxtrxSensorEntityDescription,
290  event: RFXtrxEvent | None = None,
291  ) -> None:
292  """Initialize the sensor."""
293  super().__init__(device, device_id, event=event)
294  self.entity_descriptionentity_description = entity_description
295  self._attr_unique_id_attr_unique_id_attr_unique_id = "_".join(x for x in (*device_id, entity_description.key))
296 
297  async def async_added_to_hass(self) -> None:
298  """Restore device state."""
299  await super().async_added_to_hass()
300 
301  if (
302  self._event_event is None
303  and (old_state := await self.async_get_last_stateasync_get_last_state()) is not None
304  and (event := old_state.attributes.get(ATTR_EVENT))
305  ):
306  self._apply_event_apply_event(get_rfx_object(event))
307 
308  @property
309  def native_value(self) -> StateType | date | datetime | Decimal:
310  """Return the state of the sensor."""
311  if not self._event_event:
312  return None
313  value = self._event_event.values.get(self.entity_descriptionentity_description.key)
314  return self.entity_descriptionentity_description.convert(value)
315 
316  @callback
317  def _handle_event(self, event: RFXtrxEvent, device_id: DeviceTuple) -> None:
318  """Check if event applies to me and update."""
319  if device_id != self._device_id_device_id:
320  return
321 
322  if self.entity_descriptionentity_description.key not in event.values:
323  return
324 
325  _LOGGER.debug(
326  "Sensor update (Device ID: %s Class: %s Sub: %s)",
327  event.device.id_string,
328  event.device.__class__.__name__,
329  event.device.subtype,
330  )
331 
332  self._apply_event_apply_event(event)
333 
334  self.async_write_ha_stateasync_write_ha_state()
None _apply_event(self, rfxtrxmod.RFXtrxEvent event)
Definition: entity.py:91
None _handle_event(self, RFXtrxEvent event, DeviceTuple device_id)
Definition: sensor.py:317
None __init__(self, RFXtrxDevice device, DeviceTuple device_id, RfxtrxSensorEntityDescription entity_description, RFXtrxEvent|None event=None)
Definition: sensor.py:291
StateType|date|datetime|Decimal native_value(self)
Definition: sensor.py:309
int|None _battery_convert(int|None value)
Definition: sensor.py:49
str|None _rssi_convert(int|None value)
Definition: sensor.py:56
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:247
None async_setup_platform_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities, Callable[[rfxtrxmod.RFXtrxEvent], bool] supported, Callable[[rfxtrxmod.RFXtrxEvent, rfxtrxmod.RFXtrxEvent|None, DeviceTuple, dict[str, Any],], list[Entity],] constructor)
Definition: __init__.py:308
rfxtrxmod.RFXtrxEvent|None get_rfx_object(str packetid)
Definition: __init__.py:352