Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor platform for mobile_app."""
2 
3 from __future__ import annotations
4 
5 from datetime import date, datetime
6 from typing import TYPE_CHECKING, Any
7 
8 from homeassistant.components.sensor import RestoreSensor, SensorDeviceClass
9 from homeassistant.config_entries import ConfigEntry
10 from homeassistant.const import CONF_WEBHOOK_ID, STATE_UNKNOWN, UnitOfTemperature
11 from homeassistant.core import HomeAssistant, State, callback
12 from homeassistant.helpers import entity_registry as er
13 from homeassistant.helpers.dispatcher import async_dispatcher_connect
14 from homeassistant.helpers.entity_platform import AddEntitiesCallback
15 from homeassistant.helpers.typing import StateType
16 from homeassistant.util import dt as dt_util
17 
18 from .const import (
19  ATTR_SENSOR_ATTRIBUTES,
20  ATTR_SENSOR_DEVICE_CLASS,
21  ATTR_SENSOR_ENTITY_CATEGORY,
22  ATTR_SENSOR_ICON,
23  ATTR_SENSOR_NAME,
24  ATTR_SENSOR_STATE,
25  ATTR_SENSOR_STATE_CLASS,
26  ATTR_SENSOR_TYPE,
27  ATTR_SENSOR_TYPE_SENSOR as ENTITY_TYPE,
28  ATTR_SENSOR_UNIQUE_ID,
29  ATTR_SENSOR_UOM,
30  DOMAIN,
31 )
32 from .entity import MobileAppEntity
33 from .webhook import _extract_sensor_unique_id
34 
35 
37  hass: HomeAssistant,
38  config_entry: ConfigEntry,
39  async_add_entities: AddEntitiesCallback,
40 ) -> None:
41  """Set up mobile app sensor from a config entry."""
42  entities = []
43 
44  webhook_id = config_entry.data[CONF_WEBHOOK_ID]
45 
46  entity_registry = er.async_get(hass)
47  entries = er.async_entries_for_config_entry(entity_registry, config_entry.entry_id)
48  for entry in entries:
49  if entry.domain != ENTITY_TYPE or entry.disabled_by:
50  continue
51  config: dict[str, Any] = {
52  ATTR_SENSOR_ATTRIBUTES: {},
53  ATTR_SENSOR_DEVICE_CLASS: entry.device_class or entry.original_device_class,
54  ATTR_SENSOR_ICON: entry.original_icon,
55  ATTR_SENSOR_NAME: entry.original_name,
56  ATTR_SENSOR_STATE: None,
57  ATTR_SENSOR_TYPE: entry.domain,
58  ATTR_SENSOR_UNIQUE_ID: entry.unique_id,
59  ATTR_SENSOR_UOM: entry.unit_of_measurement,
60  ATTR_SENSOR_ENTITY_CATEGORY: entry.entity_category,
61  }
62  if capabilities := entry.capabilities:
63  config[ATTR_SENSOR_STATE_CLASS] = capabilities.get(ATTR_SENSOR_STATE_CLASS)
64  entities.append(MobileAppSensor(config, config_entry))
65 
66  async_add_entities(entities)
67 
68  @callback
69  def handle_sensor_registration(data):
70  if data[CONF_WEBHOOK_ID] != webhook_id:
71  return
72 
73  async_add_entities([MobileAppSensor(data, config_entry)])
74 
76  hass,
77  f"{DOMAIN}_{ENTITY_TYPE}_register",
78  handle_sensor_registration,
79  )
80 
81 
83  """Representation of a mobile app sensor."""
84 
85  async def async_restore_last_state(self, last_state: State) -> None:
86  """Restore previous state."""
87  await super().async_restore_last_state(last_state)
88  config = self._config_config
89  if not (last_sensor_data := await self.async_get_last_sensor_dataasync_get_last_sensor_data()):
90  # Workaround to handle migration to RestoreSensor, can be removed
91  # in HA Core 2023.4
92  config[ATTR_SENSOR_STATE] = None
93  webhook_id = self._entry_entry.data[CONF_WEBHOOK_ID]
94  if TYPE_CHECKING:
95  assert self.unique_idunique_id is not None
96  sensor_unique_id = _extract_sensor_unique_id(webhook_id, self.unique_idunique_id)
97  if (
98  self.device_classdevice_classdevice_classdevice_class == SensorDeviceClass.TEMPERATURE
99  and sensor_unique_id == "battery_temperature"
100  ):
101  config[ATTR_SENSOR_UOM] = UnitOfTemperature.CELSIUS
102  else:
103  config[ATTR_SENSOR_STATE] = last_sensor_data.native_value
104  config[ATTR_SENSOR_UOM] = last_sensor_data.native_unit_of_measurement
105 
106  self._async_update_attr_from_config_async_update_attr_from_config()
107 
108  def _calculate_native_value(self) -> StateType | date | datetime:
109  """Return the state of the sensor."""
110  if (state := self._config_config[ATTR_SENSOR_STATE]) in (None, STATE_UNKNOWN):
111  return None
112 
113  device_class = self.device_classdevice_classdevice_classdevice_class
114 
115  if (
116  device_class in (SensorDeviceClass.DATE, SensorDeviceClass.TIMESTAMP)
117  # Only parse strings: if the sensor's state is restored, the state is a
118  # native date or datetime, not str
119  and isinstance(state, str)
120  and (timestamp := dt_util.parse_datetime(state)) is not None
121  ):
122  if device_class == SensorDeviceClass.DATE:
123  return timestamp.date()
124  return timestamp
125 
126  return state
127 
128  @callback
129  def _async_update_attr_from_config(self) -> None:
130  """Update the entity from the config."""
132  config = self._config_config
133  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = config.get(ATTR_SENSOR_UOM)
134  self._attr_state_class_attr_state_class_attr_state_class = config.get(ATTR_SENSOR_STATE_CLASS)
135  self._attr_native_value_attr_native_value = self._calculate_native_value()
None async_restore_last_state(self, State last_state)
Definition: sensor.py:85
SensorExtraStoredData|None async_get_last_sensor_data(self)
Definition: __init__.py:934
SensorDeviceClass|None device_class(self)
Definition: __init__.py:313
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:40
str _extract_sensor_unique_id(str webhook_id, str unique_id)
Definition: webhook.py:516
Callable[[], None] async_dispatcher_connect(HomeAssistant hass, str signal, Callable[..., Any] target)
Definition: dispatcher.py:103