Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Platform allowing several sensors to be grouped into one sensor to provide numeric combinations."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from datetime import datetime
7 import logging
8 import statistics
9 from typing import TYPE_CHECKING, Any
10 
11 import voluptuous as vol
12 
13 from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN
14 from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
16  CONF_STATE_CLASS,
17  DEVICE_CLASS_UNITS,
18  DEVICE_CLASSES_SCHEMA,
19  DOMAIN as SENSOR_DOMAIN,
20  PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
21  STATE_CLASSES_SCHEMA,
22  UNIT_CONVERTERS,
23  SensorDeviceClass,
24  SensorEntity,
25  SensorStateClass,
26 )
27 from homeassistant.config_entries import ConfigEntry
28 from homeassistant.const import (
29  ATTR_ENTITY_ID,
30  CONF_DEVICE_CLASS,
31  CONF_ENTITIES,
32  CONF_NAME,
33  CONF_TYPE,
34  CONF_UNIQUE_ID,
35  CONF_UNIT_OF_MEASUREMENT,
36  STATE_UNAVAILABLE,
37  STATE_UNKNOWN,
38 )
39 from homeassistant.core import HomeAssistant, State, callback
40 from homeassistant.exceptions import HomeAssistantError
41 from homeassistant.helpers import config_validation as cv, entity_registry as er
42 from homeassistant.helpers.entity import (
43  get_capability,
44  get_device_class,
45  get_unit_of_measurement,
46 )
47 from homeassistant.helpers.entity_platform import AddEntitiesCallback
49  IssueSeverity,
50  async_create_issue,
51  async_delete_issue,
52 )
53 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
54 
55 from .const import CONF_IGNORE_NON_NUMERIC, DOMAIN as GROUP_DOMAIN
56 from .entity import GroupEntity
57 
58 DEFAULT_NAME = "Sensor Group"
59 
60 ATTR_MIN_VALUE = "min_value"
61 ATTR_MIN_ENTITY_ID = "min_entity_id"
62 ATTR_MAX_VALUE = "max_value"
63 ATTR_MAX_ENTITY_ID = "max_entity_id"
64 ATTR_MEAN = "mean"
65 ATTR_MEDIAN = "median"
66 ATTR_LAST = "last"
67 ATTR_LAST_ENTITY_ID = "last_entity_id"
68 ATTR_RANGE = "range"
69 ATTR_STDEV = "stdev"
70 ATTR_SUM = "sum"
71 ATTR_PRODUCT = "product"
72 SENSOR_TYPES = {
73  ATTR_MIN_VALUE: "min",
74  ATTR_MAX_VALUE: "max",
75  ATTR_MEAN: "mean",
76  ATTR_MEDIAN: "median",
77  ATTR_LAST: "last",
78  ATTR_RANGE: "range",
79  ATTR_STDEV: "stdev",
80  ATTR_SUM: "sum",
81  ATTR_PRODUCT: "product",
82 }
83 SENSOR_TYPE_TO_ATTR = {v: k for k, v in SENSOR_TYPES.items()}
84 
85 # No limit on parallel updates to enable a group calling another group
86 PARALLEL_UPDATES = 0
87 
88 PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
89  {
90  vol.Required(CONF_ENTITIES): cv.entities_domain(
91  [SENSOR_DOMAIN, NUMBER_DOMAIN, INPUT_NUMBER_DOMAIN]
92  ),
93  vol.Required(CONF_TYPE): vol.All(cv.string, vol.In(SENSOR_TYPES.values())),
94  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
95  vol.Optional(CONF_UNIQUE_ID): cv.string,
96  vol.Optional(CONF_IGNORE_NON_NUMERIC, default=False): cv.boolean,
97  vol.Optional(CONF_UNIT_OF_MEASUREMENT): str,
98  vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
99  vol.Optional(CONF_STATE_CLASS): STATE_CLASSES_SCHEMA,
100  }
101 )
102 
103 _LOGGER = logging.getLogger(__name__)
104 
105 
107  hass: HomeAssistant,
108  config: ConfigType,
109  async_add_entities: AddEntitiesCallback,
110  discovery_info: DiscoveryInfoType | None = None,
111 ) -> None:
112  """Set up the Switch Group platform."""
114  [
115  SensorGroup(
116  hass,
117  config.get(CONF_UNIQUE_ID),
118  config[CONF_NAME],
119  config[CONF_ENTITIES],
120  config[CONF_IGNORE_NON_NUMERIC],
121  config[CONF_TYPE],
122  config.get(CONF_UNIT_OF_MEASUREMENT),
123  config.get(CONF_STATE_CLASS),
124  config.get(CONF_DEVICE_CLASS),
125  )
126  ]
127  )
128 
129 
131  hass: HomeAssistant,
132  config_entry: ConfigEntry,
133  async_add_entities: AddEntitiesCallback,
134 ) -> None:
135  """Initialize Switch Group config entry."""
136  registry = er.async_get(hass)
137  entities = er.async_validate_entity_ids(
138  registry, config_entry.options[CONF_ENTITIES]
139  )
141  [
142  SensorGroup(
143  hass,
144  config_entry.entry_id,
145  config_entry.title,
146  entities,
147  config_entry.options.get(CONF_IGNORE_NON_NUMERIC, True),
148  config_entry.options[CONF_TYPE],
149  None,
150  None,
151  None,
152  )
153  ]
154  )
155 
156 
157 @callback
159  hass: HomeAssistant, name: str, validated_config: dict[str, Any]
160 ) -> SensorGroup:
161  """Create a preview sensor."""
162  return SensorGroup(
163  hass,
164  None,
165  name,
166  validated_config[CONF_ENTITIES],
167  validated_config.get(CONF_IGNORE_NON_NUMERIC, False),
168  validated_config[CONF_TYPE],
169  None,
170  None,
171  None,
172  )
173 
174 
175 def _has_numeric_state(hass: HomeAssistant, entity_id: str) -> bool:
176  """Test if state is numeric."""
177  if not (state := hass.states.get(entity_id)):
178  return False
179  try:
180  float(state.state)
181  except ValueError:
182  return False
183  return True
184 
185 
187  sensor_values: list[tuple[str, float, State]],
188 ) -> tuple[dict[str, str | None], float | None]:
189  """Calculate min value."""
190  val: float | None = None
191  entity_id: str | None = None
192  for sensor_id, sensor_value, _ in sensor_values:
193  if val is None or val > sensor_value:
194  entity_id, val = sensor_id, sensor_value
195 
196  attributes = {ATTR_MIN_ENTITY_ID: entity_id}
197  if TYPE_CHECKING:
198  assert val is not None
199  return attributes, val
200 
201 
203  sensor_values: list[tuple[str, float, State]],
204 ) -> tuple[dict[str, str | None], float | None]:
205  """Calculate max value."""
206  val: float | None = None
207  entity_id: str | None = None
208  for sensor_id, sensor_value, _ in sensor_values:
209  if val is None or val < sensor_value:
210  entity_id, val = sensor_id, sensor_value
211 
212  attributes = {ATTR_MAX_ENTITY_ID: entity_id}
213  if TYPE_CHECKING:
214  assert val is not None
215  return attributes, val
216 
217 
219  sensor_values: list[tuple[str, float, State]],
220 ) -> tuple[dict[str, str | None], float | None]:
221  """Calculate mean value."""
222  result = (sensor_value for _, sensor_value, _ in sensor_values)
223 
224  value: float = statistics.mean(result)
225  return {}, value
226 
227 
229  sensor_values: list[tuple[str, float, State]],
230 ) -> tuple[dict[str, str | None], float | None]:
231  """Calculate median value."""
232  result = (sensor_value for _, sensor_value, _ in sensor_values)
233 
234  value: float = statistics.median(result)
235  return {}, value
236 
237 
239  sensor_values: list[tuple[str, float, State]],
240 ) -> tuple[dict[str, str | None], float | None]:
241  """Calculate last value."""
242  last_updated: datetime | None = None
243  last_entity_id: str | None = None
244  last: float | None = None
245  for entity_id, state_f, state in sensor_values:
246  if last_updated is None or state.last_updated > last_updated:
247  last_updated = state.last_updated
248  last = state_f
249  last_entity_id = entity_id
250 
251  attributes = {ATTR_LAST_ENTITY_ID: last_entity_id}
252  return attributes, last
253 
254 
256  sensor_values: list[tuple[str, float, State]],
257 ) -> tuple[dict[str, str | None], float]:
258  """Calculate range value."""
259  max_result = max((sensor_value for _, sensor_value, _ in sensor_values))
260  min_result = min((sensor_value for _, sensor_value, _ in sensor_values))
261 
262  value: float = max_result - min_result
263  return {}, value
264 
265 
267  sensor_values: list[tuple[str, float, State]],
268 ) -> tuple[dict[str, str | None], float]:
269  """Calculate standard deviation value."""
270  result = (sensor_value for _, sensor_value, _ in sensor_values)
271 
272  value: float = statistics.stdev(result)
273  return {}, value
274 
275 
277  sensor_values: list[tuple[str, float, State]],
278 ) -> tuple[dict[str, str | None], float]:
279  """Calculate a sum of values."""
280  result = 0.0
281  for _, sensor_value, _ in sensor_values:
282  result += sensor_value
283 
284  return {}, result
285 
286 
288  sensor_values: list[tuple[str, float, State]],
289 ) -> tuple[dict[str, str | None], float]:
290  """Calculate a product of values."""
291  result = 1.0
292  for _, sensor_value, _ in sensor_values:
293  result *= sensor_value
294 
295  return {}, result
296 
297 
298 CALC_TYPES: dict[
299  str,
300  Callable[
301  [list[tuple[str, float, State]]], tuple[dict[str, str | None], float | None]
302  ],
303 ] = {
304  "min": calc_min,
305  "max": calc_max,
306  "mean": calc_mean,
307  "median": calc_median,
308  "last": calc_last,
309  "range": calc_range,
310  "stdev": calc_stdev,
311  "sum": calc_sum,
312  "product": calc_product,
313 }
314 
315 
317  """Representation of a sensor group."""
318 
319  _attr_available = False
320  _attr_should_poll = False
321 
322  def __init__(
323  self,
324  hass: HomeAssistant,
325  unique_id: str | None,
326  name: str,
327  entity_ids: list[str],
328  ignore_non_numeric: bool,
329  sensor_type: str,
330  unit_of_measurement: str | None,
331  state_class: SensorStateClass | None,
332  device_class: SensorDeviceClass | None,
333  ) -> None:
334  """Initialize a sensor group."""
335  self.hasshasshass = hass
336  self._entity_ids_entity_ids = entity_ids
337  self._sensor_type_sensor_type = sensor_type
338  self._configured_state_class_configured_state_class = state_class
339  self._configured_device_class_configured_device_class = device_class
340  self._configured_unit_of_measurement_configured_unit_of_measurement = unit_of_measurement
341  self._valid_units_valid_units: set[str | None] = set()
342  self._can_convert_can_convert: bool = False
343  self._attr_name_attr_name = name
344  if name == DEFAULT_NAME:
345  self._attr_name_attr_name = f"{DEFAULT_NAME} {sensor_type}".capitalize()
346  self._attr_extra_state_attributes_attr_extra_state_attributes = {ATTR_ENTITY_ID: entity_ids}
347  self._attr_unique_id_attr_unique_id = unique_id
348  self._ignore_non_numeric_ignore_non_numeric = ignore_non_numeric
349  self.modemode = all if ignore_non_numeric is False else any
350  self._state_calc: Callable[
351  [list[tuple[str, float, State]]],
352  tuple[dict[str, str | None], float | None],
353  ] = CALC_TYPES[self._sensor_type_sensor_type]
354  self._state_incorrect: set[str] = set()
355  self._extra_state_attribute: dict[str, Any] = {}
356 
357  def calculate_state_attributes(self, valid_state_entities: list[str]) -> None:
358  """Calculate state attributes."""
359  self._attr_state_class_attr_state_class = self._calculate_state_class_calculate_state_class(
360  self._configured_state_class_configured_state_class, valid_state_entities
361  )
362  self._attr_device_class_attr_device_class = self._calculate_device_class_calculate_device_class(
363  self._configured_device_class_configured_device_class, valid_state_entities
364  )
365  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = self._calculate_unit_of_measurement_calculate_unit_of_measurement(
366  self._configured_unit_of_measurement_configured_unit_of_measurement, valid_state_entities
367  )
368  self._valid_units_valid_units = self._get_valid_units_get_valid_units()
369 
370  @callback
371  def async_update_group_state(self) -> None:
372  """Query all members and determine the sensor group state."""
373  self.calculate_state_attributescalculate_state_attributes(self._get_valid_entities_get_valid_entities())
374  states: list[StateType] = []
375  valid_units = self._valid_units_valid_units
376  valid_states: list[bool] = []
377  sensor_values: list[tuple[str, float, State]] = []
378  for entity_id in self._entity_ids_entity_ids:
379  if (state := self.hasshasshass.states.get(entity_id)) is not None:
380  states.append(state.state)
381  try:
382  numeric_state = float(state.state)
383  uom = state.attributes.get("unit_of_measurement")
384 
385  # Convert the state to the native unit of measurement when we have valid units
386  # and a correct device class
387  if valid_units and uom in valid_units and self._can_convert_can_convert is True:
388  numeric_state = UNIT_CONVERTERS[self.device_classdevice_classdevice_class].convert(
389  numeric_state, uom, self.native_unit_of_measurementnative_unit_of_measurement
390  )
391 
392  # If we have valid units and the entity's unit does not match
393  # we raise which skips the state and log a warning once
394  if valid_units and uom not in valid_units:
395  raise HomeAssistantError("Not a valid unit") # noqa: TRY301
396 
397  sensor_values.append((entity_id, numeric_state, state))
398  if entity_id in self._state_incorrect:
399  self._state_incorrect.remove(entity_id)
400  valid_states.append(True)
401  except ValueError:
402  valid_states.append(False)
403  # Log invalid states unless ignoring non numeric values
404  if (
405  not self._ignore_non_numeric_ignore_non_numeric
406  and entity_id not in self._state_incorrect
407  ):
408  self._state_incorrect.add(entity_id)
409  _LOGGER.warning(
410  "Unable to use state. Only numerical states are supported,"
411  " entity %s with value %s excluded from calculation in %s",
412  entity_id,
413  state.state,
414  self.entity_identity_id,
415  )
416  continue
417  except (KeyError, HomeAssistantError):
418  # This exception handling can be simplified
419  # once sensor entity doesn't allow incorrect unit of measurement
420  # with a device class, implementation see PR #107639
421  valid_states.append(False)
422  if entity_id not in self._state_incorrect:
423  self._state_incorrect.add(entity_id)
424  _LOGGER.warning(
425  "Unable to use state. Only entities with correct unit of measurement"
426  " is supported,"
427  " entity %s, value %s with device class %s"
428  " and unit of measurement %s excluded from calculation in %s",
429  entity_id,
430  state.state,
431  self.device_classdevice_classdevice_class,
432  state.attributes.get("unit_of_measurement"),
433  self.entity_identity_id,
434  )
435 
436  # Set group as unavailable if all members do not have numeric values
437  self._attr_available_attr_available_attr_available = any(numeric_state for numeric_state in valid_states)
438 
439  valid_state = self.modemode(
440  state not in (STATE_UNKNOWN, STATE_UNAVAILABLE) for state in states
441  )
442  valid_state_numeric = self.modemode(numeric_state for numeric_state in valid_states)
443 
444  if not valid_state or not valid_state_numeric:
445  self._attr_native_value_attr_native_value = None
446  return
447 
448  # Calculate values
449  self._extra_state_attribute, self._attr_native_value_attr_native_value = self._state_calc(
450  sensor_values
451  )
452 
453  @property
454  def extra_state_attributes(self) -> dict[str, Any]:
455  """Return the state attributes of the sensor."""
456  return {ATTR_ENTITY_ID: self._entity_ids_entity_ids, **self._extra_state_attribute}
457 
458  @property
459  def icon(self) -> str | None:
460  """Return the icon.
461 
462  Only override the icon if the device class is not set.
463  """
464  if not self.device_classdevice_classdevice_class:
465  return "mdi:calculator"
466  return None
467 
469  self,
470  state_class: SensorStateClass | None,
471  valid_state_entities: list[str],
472  ) -> SensorStateClass | None:
473  """Calculate state class.
474 
475  If user has configured a state class we will use that.
476  If a state class is not set then test if same state class
477  on source entities and use that.
478  Otherwise return no state class.
479  """
480  if state_class:
481  return state_class
482 
483  if not valid_state_entities:
484  return None
485 
486  if not self._ignore_non_numeric_ignore_non_numeric and len(valid_state_entities) < len(
487  self._entity_ids_entity_ids
488  ):
489  # Only return state class if all states are valid when not ignoring non numeric
490  return None
491 
492  state_classes: list[SensorStateClass] = []
493  for entity_id in valid_state_entities:
494  try:
495  _state_class = get_capability(self.hasshasshass, entity_id, "state_class")
496  except HomeAssistantError:
497  return None
498  if not _state_class:
499  return None
500  state_classes.append(_state_class)
501 
502  if all(x == state_classes[0] for x in state_classes):
504  self.hasshasshass, SENSOR_DOMAIN, f"{self.entity_id}_state_classes_not_matching"
505  )
506  return state_classes[0]
508  self.hasshasshass,
509  GROUP_DOMAIN,
510  f"{self.entity_id}_state_classes_not_matching",
511  is_fixable=False,
512  is_persistent=False,
513  severity=IssueSeverity.WARNING,
514  translation_key="state_classes_not_matching",
515  translation_placeholders={
516  "entity_id": self.entity_identity_id,
517  "source_entities": ", ".join(self._entity_ids_entity_ids),
518  "state_classes": ", ".join(state_classes),
519  },
520  )
521  return None
522 
524  self,
525  device_class: SensorDeviceClass | None,
526  valid_state_entities: list[str],
527  ) -> SensorDeviceClass | None:
528  """Calculate device class.
529 
530  If user has configured a device class we will use that.
531  If a device class is not set then test if same device class
532  on source entities and use that.
533  Otherwise return no device class.
534  """
535  if device_class:
536  return device_class
537 
538  if not valid_state_entities:
539  return None
540 
541  if not self._ignore_non_numeric_ignore_non_numeric and len(valid_state_entities) < len(
542  self._entity_ids_entity_ids
543  ):
544  # Only return device class if all states are valid when not ignoring non numeric
545  return None
546 
547  device_classes: list[SensorDeviceClass] = []
548  for entity_id in valid_state_entities:
549  try:
550  _device_class = get_device_class(self.hasshasshass, entity_id)
551  except HomeAssistantError:
552  return None
553  if not _device_class:
554  return None
555  device_classes.append(SensorDeviceClass(_device_class))
556 
557  if all(x == device_classes[0] for x in device_classes):
559  self.hasshasshass,
560  SENSOR_DOMAIN,
561  f"{self.entity_id}_device_classes_not_matching",
562  )
563  return device_classes[0]
565  self.hasshasshass,
566  GROUP_DOMAIN,
567  f"{self.entity_id}_device_classes_not_matching",
568  is_fixable=False,
569  is_persistent=False,
570  severity=IssueSeverity.WARNING,
571  translation_key="device_classes_not_matching",
572  translation_placeholders={
573  "entity_id": self.entity_identity_id,
574  "source_entities": ", ".join(self._entity_ids_entity_ids),
575  "device_classes": ", ".join(device_classes),
576  },
577  )
578  return None
579 
581  self,
582  unit_of_measurement: str | None,
583  valid_state_entities: list[str],
584  ) -> str | None:
585  """Calculate the unit of measurement.
586 
587  If user has configured a unit of measurement we will use that.
588  If a device class is set then test if unit of measurements are compatible.
589  If no device class or uom's not compatible we will use no unit of measurement.
590  """
591  if unit_of_measurement:
592  return unit_of_measurement
593 
594  if not valid_state_entities:
595  return None
596 
597  if not self._ignore_non_numeric_ignore_non_numeric and len(valid_state_entities) < len(
598  self._entity_ids_entity_ids
599  ):
600  # Only return device class if all states are valid when not ignoring non numeric
601  return None
602 
603  unit_of_measurements: list[str] = []
604  for entity_id in valid_state_entities:
605  try:
606  _unit_of_measurement = get_unit_of_measurement(self.hasshasshass, entity_id)
607  except HomeAssistantError:
608  return None
609  if not _unit_of_measurement:
610  return None
611  unit_of_measurements.append(_unit_of_measurement)
612 
613  # Ensure only valid unit of measurements for the specific device class can be used
614  if (
615  (
616  # Test if uom's in device class is convertible
617  (device_class := self.device_classdevice_classdevice_class) in UNIT_CONVERTERS
618  and all(
619  uom in UNIT_CONVERTERS[device_class].VALID_UNITS
620  for uom in unit_of_measurements
621  )
622  )
623  or (
624  # Test if uom's in device class is not convertible
625  device_class
626  and device_class not in UNIT_CONVERTERS
627  and device_class in DEVICE_CLASS_UNITS
628  and all(
629  uom in DEVICE_CLASS_UNITS[device_class]
630  for uom in unit_of_measurements
631  )
632  )
633  or (
634  # Test no device class and all uom's are same
635  device_class is None
636  and all(x == unit_of_measurements[0] for x in unit_of_measurements)
637  )
638  ):
640  self.hasshasshass,
641  SENSOR_DOMAIN,
642  f"{self.entity_id}_uoms_not_matching_device_class",
643  )
645  self.hasshasshass,
646  SENSOR_DOMAIN,
647  f"{self.entity_id}_uoms_not_matching_no_device_class",
648  )
649  return unit_of_measurements[0]
650 
651  if device_class:
653  self.hasshasshass,
654  GROUP_DOMAIN,
655  f"{self.entity_id}_uoms_not_matching_device_class",
656  is_fixable=False,
657  is_persistent=False,
658  severity=IssueSeverity.WARNING,
659  translation_key="uoms_not_matching_device_class",
660  translation_placeholders={
661  "entity_id": self.entity_identity_id,
662  "device_class": device_class,
663  "source_entities": ", ".join(self._entity_ids_entity_ids),
664  "uoms": ", ".join(unit_of_measurements),
665  },
666  )
667  else:
669  self.hasshasshass,
670  GROUP_DOMAIN,
671  f"{self.entity_id}_uoms_not_matching_no_device_class",
672  is_fixable=False,
673  is_persistent=False,
674  severity=IssueSeverity.WARNING,
675  translation_key="uoms_not_matching_no_device_class",
676  translation_placeholders={
677  "entity_id": self.entity_identity_id,
678  "source_entities": ", ".join(self._entity_ids_entity_ids),
679  "uoms": ", ".join(unit_of_measurements),
680  },
681  )
682  return None
683 
684  def _get_valid_units(self) -> set[str | None]:
685  """Return valid units.
686 
687  If device class is set and compatible unit of measurements.
688  If device class is not set, use one unit of measurement.
689  Only calculate valid units if there are no valid units set.
690  """
691  if (valid_units := self._valid_units_valid_units) and not self._ignore_non_numeric_ignore_non_numeric:
692  # If we have valid units already and not using ignore_non_numeric
693  # we should not recalculate.
694  return valid_units
695 
696  native_uom = self.native_unit_of_measurementnative_unit_of_measurement
697  if (device_class := self.device_classdevice_classdevice_class) in UNIT_CONVERTERS and native_uom:
698  self._can_convert_can_convert = True
699  return UNIT_CONVERTERS[device_class].VALID_UNITS
700  if device_class and (device_class) in DEVICE_CLASS_UNITS and native_uom:
701  valid_uoms: set = DEVICE_CLASS_UNITS[device_class]
702  return valid_uoms
703  if device_class is None and native_uom:
704  return {native_uom}
705  return set()
706 
708  self,
709  ) -> list[str]:
710  """Return list of valid entities."""
711 
712  return [
713  entity_id
714  for entity_id in self._entity_ids_entity_ids
715  if _has_numeric_state(self.hasshasshass, entity_id)
716  ]
str|None _calculate_unit_of_measurement(self, str|None unit_of_measurement, list[str] valid_state_entities)
Definition: sensor.py:584
None __init__(self, HomeAssistant hass, str|None unique_id, str name, list[str] entity_ids, bool ignore_non_numeric, str sensor_type, str|None unit_of_measurement, SensorStateClass|None state_class, SensorDeviceClass|None device_class)
Definition: sensor.py:333
SensorStateClass|None _calculate_state_class(self, SensorStateClass|None state_class, list[str] valid_state_entities)
Definition: sensor.py:472
SensorDeviceClass|None _calculate_device_class(self, SensorDeviceClass|None device_class, list[str] valid_state_entities)
Definition: sensor.py:527
None calculate_state_attributes(self, list[str] valid_state_entities)
Definition: sensor.py:357
SensorDeviceClass|None device_class(self)
Definition: __init__.py:313
bool add(self, _T matcher)
Definition: match.py:185
bool remove(self, _T matcher)
Definition: match.py:214
tuple[dict[str, str|None], float] calc_stdev(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:268
tuple[dict[str, str|None], float|None] calc_min(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:188
tuple[dict[str, str|None], float|None] calc_mean(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:220
SensorGroup async_create_preview_sensor(HomeAssistant hass, str name, dict[str, Any] validated_config)
Definition: sensor.py:160
tuple[dict[str, str|None], float|None] calc_max(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:204
tuple[dict[str, str|None], float] calc_sum(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:278
tuple[dict[str, str|None], float|None] calc_median(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:230
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
Definition: sensor.py:111
tuple[dict[str, str|None], float] calc_range(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:257
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:134
tuple[dict[str, str|None], float|None] calc_last(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:240
bool _has_numeric_state(HomeAssistant hass, str entity_id)
Definition: sensor.py:175
tuple[dict[str, str|None], float] calc_product(list[tuple[str, float, State]] sensor_values)
Definition: sensor.py:289
None async_create_issue(HomeAssistant hass, str entry_id)
Definition: repairs.py:69
None async_delete_issue(HomeAssistant hass, str entry_id)
Definition: repairs.py:85
str|None get_device_class(HomeAssistant hass, str entity_id)
Definition: entity.py:154
Any|None get_capability(HomeAssistant hass, str entity_id, str capability)
Definition: entity.py:139
str|None get_unit_of_measurement(HomeAssistant hass, str entity_id)
Definition: entity.py:184