Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for KNX/IP sensors."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import datetime, timedelta
8 from functools import partial
9 from typing import Any
10 
11 from xknx import XKNX
12 from xknx.core.connection_state import XknxConnectionState, XknxConnectionType
13 from xknx.devices import Sensor as XknxSensor
14 
15 from homeassistant import config_entries
17  CONF_STATE_CLASS,
18  SensorDeviceClass,
19  SensorEntity,
20  SensorEntityDescription,
21  SensorStateClass,
22 )
23 from homeassistant.const import (
24  CONF_DEVICE_CLASS,
25  CONF_ENTITY_CATEGORY,
26  CONF_NAME,
27  CONF_TYPE,
28  EntityCategory,
29  Platform,
30 )
31 from homeassistant.core import HomeAssistant
32 from homeassistant.helpers.entity_platform import AddEntitiesCallback
33 from homeassistant.helpers.typing import ConfigType, StateType
34 from homeassistant.util.enum import try_parse_enum
35 
36 from . import KNXModule
37 from .const import ATTR_SOURCE, KNX_MODULE_KEY
38 from .entity import KnxYamlEntity
39 from .schema import SensorSchema
40 
41 SCAN_INTERVAL = timedelta(seconds=10)
42 
43 
44 @dataclass(frozen=True)
46  """Class describing KNX system sensor entities."""
47 
48  always_available: bool = True
49  entity_category: EntityCategory = EntityCategory.DIAGNOSTIC
50  has_entity_name: bool = True
51  should_poll: bool = True
52  value_fn: Callable[[KNXModule], StateType | datetime] = lambda knx: None
53 
54 
55 SYSTEM_ENTITY_DESCRIPTIONS = (
57  key="individual_address",
58  always_available=False,
59  should_poll=False,
60  value_fn=lambda knx: str(knx.xknx.current_address),
61  ),
63  key="connected_since",
64  always_available=False,
65  device_class=SensorDeviceClass.TIMESTAMP,
66  should_poll=False,
67  value_fn=lambda knx: knx.xknx.connection_manager.connected_since,
68  ),
70  key="connection_type",
71  always_available=False,
72  device_class=SensorDeviceClass.ENUM,
73  options=[opt.value for opt in XknxConnectionType],
74  should_poll=False,
75  value_fn=lambda knx: knx.xknx.connection_manager.connection_type.value,
76  ),
78  key="telegrams_incoming",
79  entity_registry_enabled_default=False,
80  force_update=True,
81  state_class=SensorStateClass.TOTAL_INCREASING,
82  value_fn=lambda knx: knx.xknx.connection_manager.cemi_count_incoming,
83  ),
85  key="telegrams_incoming_error",
86  state_class=SensorStateClass.TOTAL_INCREASING,
87  value_fn=lambda knx: knx.xknx.connection_manager.cemi_count_incoming_error,
88  ),
90  key="telegrams_outgoing",
91  entity_registry_enabled_default=False,
92  force_update=True,
93  state_class=SensorStateClass.TOTAL_INCREASING,
94  value_fn=lambda knx: knx.xknx.connection_manager.cemi_count_outgoing,
95  ),
97  key="telegrams_outgoing_error",
98  state_class=SensorStateClass.TOTAL_INCREASING,
99  value_fn=lambda knx: knx.xknx.connection_manager.cemi_count_outgoing_error,
100  ),
102  key="telegram_count",
103  force_update=True,
104  state_class=SensorStateClass.TOTAL_INCREASING,
105  value_fn=lambda knx: knx.xknx.connection_manager.cemi_count_outgoing
106  + knx.xknx.connection_manager.cemi_count_incoming
107  + knx.xknx.connection_manager.cemi_count_incoming_error,
108  ),
109 )
110 
111 
113  hass: HomeAssistant,
114  config_entry: config_entries.ConfigEntry,
115  async_add_entities: AddEntitiesCallback,
116 ) -> None:
117  """Set up sensor(s) for KNX platform."""
118  knx_module = hass.data[KNX_MODULE_KEY]
119  entities: list[SensorEntity] = []
120  entities.extend(
121  KNXSystemSensor(knx_module, description)
122  for description in SYSTEM_ENTITY_DESCRIPTIONS
123  )
124  config: list[ConfigType] | None = knx_module.config_yaml.get(Platform.SENSOR)
125  if config:
126  entities.extend(
127  KNXSensor(knx_module, entity_config) for entity_config in config
128  )
129  async_add_entities(entities)
130 
131 
132 def _create_sensor(xknx: XKNX, config: ConfigType) -> XknxSensor:
133  """Return a KNX sensor to be used within XKNX."""
134  return XknxSensor(
135  xknx,
136  name=config[CONF_NAME],
137  group_address_state=config[SensorSchema.CONF_STATE_ADDRESS],
138  sync_state=config[SensorSchema.CONF_SYNC_STATE],
139  always_callback=config[SensorSchema.CONF_ALWAYS_CALLBACK],
140  value_type=config[CONF_TYPE],
141  )
142 
143 
145  """Representation of a KNX sensor."""
146 
147  _device: XknxSensor
148 
149  def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
150  """Initialize of a KNX sensor."""
151  super().__init__(
152  knx_module=knx_module,
153  device=_create_sensor(knx_module.xknx, config),
154  )
155  if device_class := config.get(CONF_DEVICE_CLASS):
156  self._attr_device_class_attr_device_class = device_class
157  else:
158  self._attr_device_class_attr_device_class = try_parse_enum(
159  SensorDeviceClass, self._device_device.ha_device_class()
160  )
161 
162  self._attr_force_update_attr_force_update = self._device_device.always_callback
163  self._attr_entity_category_attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
164  self._attr_unique_id_attr_unique_id = str(self._device_device.sensor_value.group_address_state)
165  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = self._device_device.unit_of_measurement()
166  self._attr_state_class_attr_state_class = config.get(CONF_STATE_CLASS)
167 
168  @property
169  def native_value(self) -> StateType:
170  """Return the state of the sensor."""
171  return self._device_device.resolve_state()
172 
173  @property
174  def extra_state_attributes(self) -> dict[str, Any] | None:
175  """Return device specific state attributes."""
176  attr: dict[str, Any] = {}
177 
178  if self._device_device.last_telegram is not None:
179  attr[ATTR_SOURCE] = str(self._device_device.last_telegram.source_address)
180  return attr
181 
182 
184  """Representation of a KNX system sensor."""
185 
186  _attr_has_entity_name = True
187 
188  def __init__(
189  self,
190  knx: KNXModule,
191  description: KNXSystemEntityDescription,
192  ) -> None:
193  """Initialize of a KNX system sensor."""
194  self.entity_description: KNXSystemEntityDescription = description
195  self.knxknx = knx
196 
197  self._attr_device_info_attr_device_info = knx.interface_device.device_info
198  self._attr_should_poll_attr_should_poll = description.should_poll
199  self._attr_translation_key_attr_translation_key = description.key
200  self._attr_unique_id_attr_unique_id = f"_{knx.entry.entry_id}_{description.key}"
201 
202  @property
203  def native_value(self) -> StateType | datetime:
204  """Return the state of the sensor."""
205  return self.entity_description.value_fn(self.knxknx)
206 
207  @property
208  def available(self) -> bool:
209  """Return True if entity is available."""
210  if self.entity_description.always_available:
211  return True
212  return self.knxknx.xknx.connection_manager.state is XknxConnectionState.CONNECTED
213 
214  def after_update_callback(self, _: XknxConnectionState) -> None:
215  """Call after device was updated."""
216  self.async_write_ha_stateasync_write_ha_state()
217 
218  async def async_added_to_hass(self) -> None:
219  """Store register state change callback."""
220  self.knxknx.xknx.connection_manager.register_connection_state_changed_cb(
221  self.after_update_callbackafter_update_callback
222  )
223  self.async_on_removeasync_on_remove(
224  partial(
225  self.knxknx.xknx.connection_manager.unregister_connection_state_changed_cb,
226  self.after_update_callbackafter_update_callback,
227  )
228  )
None __init__(self, KNXModule knx_module, ConfigType config)
Definition: sensor.py:149
dict[str, Any]|None extra_state_attributes(self)
Definition: sensor.py:174
None __init__(self, KNXModule knx, KNXSystemEntityDescription description)
Definition: sensor.py:192
None after_update_callback(self, XknxConnectionState _)
Definition: sensor.py:214
None async_on_remove(self, CALLBACK_TYPE func)
Definition: entity.py:1331
None async_setup_entry(HomeAssistant hass, config_entries.ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:116
XknxSensor _create_sensor(XKNX xknx, ConfigType config)
Definition: sensor.py:132