Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for esphome sensors."""
2 
3 from __future__ import annotations
4 
5 from datetime import date, datetime
6 import math
7 
8 from aioesphomeapi import (
9  EntityInfo,
10  SensorInfo,
11  SensorState,
12  SensorStateClass as EsphomeSensorStateClass,
13  TextSensorInfo,
14  TextSensorState,
15 )
16 from aioesphomeapi.model import LastResetType
17 
19  SensorDeviceClass,
20  SensorEntity,
21  SensorStateClass,
22 )
23 from homeassistant.config_entries import ConfigEntry
24 from homeassistant.core import HomeAssistant, callback
25 from homeassistant.helpers.entity_platform import AddEntitiesCallback
26 from homeassistant.util import dt as dt_util
27 from homeassistant.util.enum import try_parse_enum
28 
29 from .entity import EsphomeEntity, platform_async_setup_entry
30 from .enum_mapper import EsphomeEnumMapper
31 
32 
34  hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
35 ) -> None:
36  """Set up esphome sensors based on a config entry."""
38  hass,
39  entry,
40  async_add_entities,
41  info_type=SensorInfo,
42  entity_type=EsphomeSensor,
43  state_type=SensorState,
44  )
46  hass,
47  entry,
48  async_add_entities,
49  info_type=TextSensorInfo,
50  entity_type=EsphomeTextSensor,
51  state_type=TextSensorState,
52  )
53 
54 
55 _STATE_CLASSES: EsphomeEnumMapper[EsphomeSensorStateClass, SensorStateClass | None] = (
57  {
58  EsphomeSensorStateClass.NONE: None,
59  EsphomeSensorStateClass.MEASUREMENT: SensorStateClass.MEASUREMENT,
60  EsphomeSensorStateClass.TOTAL_INCREASING: SensorStateClass.TOTAL_INCREASING,
61  EsphomeSensorStateClass.TOTAL: SensorStateClass.TOTAL,
62  }
63  )
64 )
65 
66 
67 class EsphomeSensor(EsphomeEntity[SensorInfo, SensorState], SensorEntity):
68  """A sensor implementation for esphome."""
69 
70  @callback
71  def _on_static_info_update(self, static_info: EntityInfo) -> None:
72  """Set attrs from static info."""
73  super()._on_static_info_update(static_info)
74  static_info = self._static_info_static_info
75  self._attr_force_update_attr_force_update = static_info.force_update
76  # protobuf doesn't support nullable strings so we need to check
77  # if the string is empty
78  if unit_of_measurement := static_info.unit_of_measurement:
79  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = unit_of_measurement
80  self._attr_device_class_attr_device_class = try_parse_enum(
81  SensorDeviceClass, static_info.device_class
82  )
83  if not (state_class := static_info.state_class):
84  return
85  if (
86  state_class == EsphomeSensorStateClass.MEASUREMENT
87  and static_info.last_reset_type == LastResetType.AUTO
88  ):
89  # Legacy, last_reset_type auto was the equivalent to the
90  # TOTAL_INCREASING state class
91  self._attr_state_class_attr_state_class = SensorStateClass.TOTAL_INCREASING
92  else:
93  self._attr_state_class_attr_state_class = _STATE_CLASSES.from_esphome(state_class)
94 
95  @property
96  def native_value(self) -> datetime | str | None:
97  """Return the state of the entity."""
98  if not self._has_state_has_state or (state := self._state_state).missing_state:
99  return None
100  state_float = state.state
101  if not math.isfinite(state_float):
102  return None
103  if self.device_classdevice_classdevice_class is SensorDeviceClass.TIMESTAMP:
104  return dt_util.utc_from_timestamp(state_float)
105  return f"{state_float:.{self._static_info.accuracy_decimals}f}"
106 
107 
108 class EsphomeTextSensor(EsphomeEntity[TextSensorInfo, TextSensorState], SensorEntity):
109  """A text sensor implementation for ESPHome."""
110 
111  @callback
112  def _on_static_info_update(self, static_info: EntityInfo) -> None:
113  """Set attrs from static info."""
114  super()._on_static_info_update(static_info)
115  static_info = self._static_info_static_info
116  self._attr_device_class_attr_device_class = try_parse_enum(
117  SensorDeviceClass, static_info.device_class
118  )
119 
120  @property
121  def native_value(self) -> str | datetime | date | None:
122  """Return the state of the entity."""
123  if not self._has_state_has_state or (state := self._state_state).missing_state:
124  return None
125  state_str = state.state
126  device_class = self.device_classdevice_classdevice_class
127  if device_class is SensorDeviceClass.TIMESTAMP:
128  return dt_util.parse_datetime(state_str)
129  if (
130  device_class is SensorDeviceClass.DATE
131  and (value := dt_util.parse_datetime(state_str)) is not None
132  ):
133  return value.date()
134  return state_str
None _on_static_info_update(self, EntityInfo static_info)
Definition: sensor.py:71
None _on_static_info_update(self, EntityInfo static_info)
Definition: sensor.py:112
SensorDeviceClass|None device_class(self)
Definition: __init__.py:313
None platform_async_setup_entry(HomeAssistant hass, ESPHomeConfigEntry entry, AddEntitiesCallback async_add_entities, *type[_InfoT] info_type, type[_EntityT] entity_type, type[_StateT] state_type)
Definition: entity.py:89
None async_setup_entry(HomeAssistant hass, ConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:35