Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for compensation sensor."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 import numpy as np
9 
10 from homeassistant.components.sensor import SensorEntity
11 from homeassistant.const import (
12  ATTR_UNIT_OF_MEASUREMENT,
13  CONF_ATTRIBUTE,
14  CONF_MAXIMUM,
15  CONF_MINIMUM,
16  CONF_SOURCE,
17  CONF_UNIQUE_ID,
18  CONF_UNIT_OF_MEASUREMENT,
19  STATE_UNKNOWN,
20 )
21 from homeassistant.core import (
22  Event,
23  EventStateChangedData,
24  HomeAssistant,
25  State,
26  callback,
27 )
28 from homeassistant.helpers.entity_platform import AddEntitiesCallback
29 from homeassistant.helpers.event import async_track_state_change_event
30 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
31 
32 from .const import (
33  CONF_COMPENSATION,
34  CONF_POLYNOMIAL,
35  CONF_PRECISION,
36  DATA_COMPENSATION,
37  DEFAULT_NAME,
38 )
39 
40 _LOGGER = logging.getLogger(__name__)
41 
42 ATTR_COEFFICIENTS = "coefficients"
43 ATTR_SOURCE = "source"
44 ATTR_SOURCE_ATTRIBUTE = "source_attribute"
45 
46 
48  hass: HomeAssistant,
49  config: ConfigType,
50  async_add_entities: AddEntitiesCallback,
51  discovery_info: DiscoveryInfoType | None = None,
52 ) -> None:
53  """Set up the Compensation sensor."""
54  if discovery_info is None:
55  return
56 
57  compensation: str = discovery_info[CONF_COMPENSATION]
58  conf: dict[str, Any] = hass.data[DATA_COMPENSATION][compensation]
59 
60  source: str = conf[CONF_SOURCE]
61  attribute: str | None = conf.get(CONF_ATTRIBUTE)
62  name = f"{DEFAULT_NAME} {source}"
63  if attribute is not None:
64  name = f"{name} {attribute}"
65 
67  [
69  conf.get(CONF_UNIQUE_ID),
70  name,
71  source,
72  attribute,
73  conf[CONF_PRECISION],
74  conf[CONF_POLYNOMIAL],
75  conf.get(CONF_UNIT_OF_MEASUREMENT),
76  conf[CONF_MINIMUM],
77  conf[CONF_MAXIMUM],
78  )
79  ]
80  )
81 
82 
84  """Representation of a Compensation sensor."""
85 
86  _attr_should_poll = False
87 
88  def __init__(
89  self,
90  unique_id: str | None,
91  name: str,
92  source: str,
93  attribute: str | None,
94  precision: int,
95  polynomial: np.poly1d,
96  unit_of_measurement: str | None,
97  minimum: tuple[float, float] | None,
98  maximum: tuple[float, float] | None,
99  ) -> None:
100  """Initialize the Compensation sensor."""
101  self._source_entity_id_source_entity_id = source
102  self._precision_precision = precision
103  self._source_attribute_source_attribute = attribute
104  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = unit_of_measurement
105  self._poly_poly = polynomial
106  self._coefficients_coefficients = polynomial.coefficients.tolist()
107  self._attr_unique_id_attr_unique_id = unique_id
108  self._attr_name_attr_name = name
109  self._minimum_minimum = minimum
110  self._maximum_maximum = maximum
111 
112  async def async_added_to_hass(self) -> None:
113  """Handle added to Hass."""
114  self.async_on_removeasync_on_remove(
116  self.hasshass,
117  [self._source_entity_id_source_entity_id],
118  self._async_compensation_sensor_state_listener_async_compensation_sensor_state_listener,
119  )
120  )
121 
122  @property
123  def extra_state_attributes(self) -> dict[str, Any]:
124  """Return the state attributes of the sensor."""
125  ret = {
126  ATTR_SOURCE: self._source_entity_id_source_entity_id,
127  ATTR_COEFFICIENTS: self._coefficients_coefficients,
128  }
129  if self._source_attribute_source_attribute:
130  ret[ATTR_SOURCE_ATTRIBUTE] = self._source_attribute_source_attribute
131  return ret
132 
133  @callback
135  self, event: Event[EventStateChangedData]
136  ) -> None:
137  """Handle sensor state changes."""
138  new_state: State | None
139  if (new_state := event.data["new_state"]) is None:
140  return
141 
142  if self.native_unit_of_measurementnative_unit_of_measurement is None and self._source_attribute_source_attribute is None:
143  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = new_state.attributes.get(
144  ATTR_UNIT_OF_MEASUREMENT
145  )
146 
147  if self._source_attribute_source_attribute:
148  value = new_state.attributes.get(self._source_attribute_source_attribute)
149  else:
150  value = None if new_state.state == STATE_UNKNOWN else new_state.state
151  try:
152  x_value = float(value) # type: ignore[arg-type]
153  if self._minimum_minimum is not None and x_value <= self._minimum_minimum[0]:
154  y_value = self._minimum_minimum[1]
155  elif self._maximum_maximum is not None and x_value >= self._maximum_maximum[0]:
156  y_value = self._maximum_maximum[1]
157  else:
158  y_value = self._poly_poly(x_value)
159  self._attr_native_value_attr_native_value = round(y_value, self._precision_precision)
160 
161  except (ValueError, TypeError):
162  self._attr_native_value_attr_native_value = None
163  if self._source_attribute_source_attribute:
164  _LOGGER.warning(
165  "%s attribute %s is not numerical",
166  self._source_entity_id_source_entity_id,
167  self._source_attribute_source_attribute,
168  )
169  else:
170  _LOGGER.warning("%s state is not numerical", self._source_entity_id_source_entity_id)
171 
172  self.async_write_ha_stateasync_write_ha_state()
None __init__(self, str|None unique_id, str name, str source, str|None attribute, int precision, np.poly1d polynomial, str|None unit_of_measurement, tuple[float, float]|None minimum, tuple[float, float]|None maximum)
Definition: sensor.py:99
None _async_compensation_sensor_state_listener(self, Event[EventStateChangedData] event)
Definition: sensor.py:136
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:52
CALLBACK_TYPE async_track_state_change_event(HomeAssistant hass, str|Iterable[str] entity_ids, Callable[[Event[EventStateChangedData]], Any] action, HassJobType|None job_type=None)
Definition: event.py:314