Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Iskra."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass, replace
7 
8 from pyiskra.devices import Device
9 from pyiskra.helper import Counter, CounterType
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15  SensorStateClass,
16 )
17 from homeassistant.const import (
18  UnitOfApparentPower,
19  UnitOfElectricCurrent,
20  UnitOfElectricPotential,
21  UnitOfEnergy,
22  UnitOfFrequency,
23  UnitOfPower,
24  UnitOfReactivePower,
25 )
26 from homeassistant.core import HomeAssistant
27 from homeassistant.helpers.entity_platform import AddEntitiesCallback
28 
29 from . import IskraConfigEntry
30 from .const import (
31  ATTR_FREQUENCY,
32  ATTR_NON_RESETTABLE_COUNTER,
33  ATTR_PHASE1_CURRENT,
34  ATTR_PHASE1_POWER,
35  ATTR_PHASE1_VOLTAGE,
36  ATTR_PHASE2_CURRENT,
37  ATTR_PHASE2_POWER,
38  ATTR_PHASE2_VOLTAGE,
39  ATTR_PHASE3_CURRENT,
40  ATTR_PHASE3_POWER,
41  ATTR_PHASE3_VOLTAGE,
42  ATTR_RESETTABLE_COUNTER,
43  ATTR_TOTAL_ACTIVE_POWER,
44  ATTR_TOTAL_APPARENT_POWER,
45  ATTR_TOTAL_REACTIVE_POWER,
46 )
47 from .coordinator import IskraDataUpdateCoordinator
48 from .entity import IskraEntity
49 
50 
51 @dataclass(frozen=True, kw_only=True)
53  """Describes Iskra sensor entity."""
54 
55  value_func: Callable[[Device], float | None]
56 
57 
58 SENSOR_TYPES: tuple[IskraSensorEntityDescription, ...] = (
59  # Power
61  key=ATTR_TOTAL_ACTIVE_POWER,
62  translation_key="total_active_power",
63  device_class=SensorDeviceClass.POWER,
64  state_class=SensorStateClass.MEASUREMENT,
65  native_unit_of_measurement=UnitOfPower.WATT,
66  value_func=lambda device: device.measurements.total.active_power.value,
67  ),
69  key=ATTR_TOTAL_REACTIVE_POWER,
70  translation_key="total_reactive_power",
71  device_class=SensorDeviceClass.REACTIVE_POWER,
72  state_class=SensorStateClass.MEASUREMENT,
73  native_unit_of_measurement=UnitOfReactivePower.VOLT_AMPERE_REACTIVE,
74  value_func=lambda device: device.measurements.total.reactive_power.value,
75  ),
77  key=ATTR_TOTAL_APPARENT_POWER,
78  translation_key="total_apparent_power",
79  device_class=SensorDeviceClass.APPARENT_POWER,
80  state_class=SensorStateClass.MEASUREMENT,
81  native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
82  value_func=lambda device: device.measurements.total.apparent_power.value,
83  ),
85  key=ATTR_PHASE1_POWER,
86  translation_key="phase1_power",
87  device_class=SensorDeviceClass.POWER,
88  state_class=SensorStateClass.MEASUREMENT,
89  native_unit_of_measurement=UnitOfPower.WATT,
90  value_func=lambda device: device.measurements.phases[0].active_power.value,
91  ),
93  key=ATTR_PHASE2_POWER,
94  translation_key="phase2_power",
95  device_class=SensorDeviceClass.POWER,
96  state_class=SensorStateClass.MEASUREMENT,
97  native_unit_of_measurement=UnitOfPower.WATT,
98  value_func=lambda device: device.measurements.phases[1].active_power.value,
99  ),
101  key=ATTR_PHASE3_POWER,
102  translation_key="phase3_power",
103  device_class=SensorDeviceClass.POWER,
104  state_class=SensorStateClass.MEASUREMENT,
105  native_unit_of_measurement=UnitOfPower.WATT,
106  value_func=lambda device: device.measurements.phases[2].active_power.value,
107  ),
108  # Voltage
110  key=ATTR_PHASE1_VOLTAGE,
111  translation_key="phase1_voltage",
112  device_class=SensorDeviceClass.VOLTAGE,
113  state_class=SensorStateClass.MEASUREMENT,
114  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
115  value_func=lambda device: device.measurements.phases[0].voltage.value,
116  ),
118  key=ATTR_PHASE2_VOLTAGE,
119  translation_key="phase2_voltage",
120  device_class=SensorDeviceClass.VOLTAGE,
121  state_class=SensorStateClass.MEASUREMENT,
122  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
123  value_func=lambda device: device.measurements.phases[1].voltage.value,
124  ),
126  key=ATTR_PHASE3_VOLTAGE,
127  translation_key="phase3_voltage",
128  device_class=SensorDeviceClass.VOLTAGE,
129  state_class=SensorStateClass.MEASUREMENT,
130  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
131  value_func=lambda device: device.measurements.phases[2].voltage.value,
132  ),
133  # Current
135  key=ATTR_PHASE1_CURRENT,
136  translation_key="phase1_current",
137  device_class=SensorDeviceClass.CURRENT,
138  state_class=SensorStateClass.MEASUREMENT,
139  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
140  value_func=lambda device: device.measurements.phases[0].current.value,
141  ),
143  key=ATTR_PHASE2_CURRENT,
144  translation_key="phase2_current",
145  device_class=SensorDeviceClass.CURRENT,
146  state_class=SensorStateClass.MEASUREMENT,
147  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
148  value_func=lambda device: device.measurements.phases[1].current.value,
149  ),
151  key=ATTR_PHASE3_CURRENT,
152  translation_key="phase3_current",
153  device_class=SensorDeviceClass.CURRENT,
154  state_class=SensorStateClass.MEASUREMENT,
155  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
156  value_func=lambda device: device.measurements.phases[2].current.value,
157  ),
158  # Frequency
160  key=ATTR_FREQUENCY,
161  translation_key="frequency",
162  device_class=SensorDeviceClass.FREQUENCY,
163  state_class=SensorStateClass.MEASUREMENT,
164  native_unit_of_measurement=UnitOfFrequency.HERTZ,
165  value_func=lambda device: device.measurements.frequency.value,
166  ),
167 )
168 
169 
171  counter: Counter,
172  index: int,
173  entity_name: str,
174 ) -> IskraSensorEntityDescription:
175  """Dynamically create IskraSensor object as energy meter's counters are customizable."""
176 
177  key = entity_name.format(index + 1)
178 
179  if entity_name == ATTR_NON_RESETTABLE_COUNTER:
180  entity_description = IskraSensorEntityDescription(
181  key=key,
182  translation_key=key,
183  state_class=SensorStateClass.TOTAL_INCREASING,
184  value_func=lambda device: device.counters.non_resettable[index].value,
185  native_unit_of_measurement=counter.units,
186  )
187  else:
188  entity_description = IskraSensorEntityDescription(
189  key=key,
190  translation_key=key,
191  state_class=SensorStateClass.TOTAL_INCREASING,
192  value_func=lambda device: device.counters.resettable[index].value,
193  native_unit_of_measurement=counter.units,
194  )
195 
196  # Set unit of measurement and device class based on counter type
197  # HA's Energy device class supports only active energy
198  if counter.counter_type in [CounterType.ACTIVE_IMPORT, CounterType.ACTIVE_EXPORT]:
199  entity_description = replace(
200  entity_description,
201  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
202  device_class=SensorDeviceClass.ENERGY,
203  )
204 
205  return entity_description
206 
207 
209  hass: HomeAssistant,
210  entry: IskraConfigEntry,
211  async_add_entities: AddEntitiesCallback,
212 ) -> None:
213  """Set up Iskra sensors based on config_entry."""
214 
215  # Device that uses the config entry.
216  coordinators = entry.runtime_data
217 
218  entities: list[IskraSensor] = []
219 
220  # Add sensors for each device.
221  for coordinator in coordinators:
222  device = coordinator.device
223  sensors = []
224 
225  # Add measurement sensors.
226  if device.supports_measurements:
227  sensors.append(ATTR_FREQUENCY)
228  sensors.append(ATTR_TOTAL_APPARENT_POWER)
229  sensors.append(ATTR_TOTAL_ACTIVE_POWER)
230  sensors.append(ATTR_TOTAL_REACTIVE_POWER)
231  if device.phases >= 1:
232  sensors.append(ATTR_PHASE1_VOLTAGE)
233  sensors.append(ATTR_PHASE1_POWER)
234  sensors.append(ATTR_PHASE1_CURRENT)
235  if device.phases >= 2:
236  sensors.append(ATTR_PHASE2_VOLTAGE)
237  sensors.append(ATTR_PHASE2_POWER)
238  sensors.append(ATTR_PHASE2_CURRENT)
239  if device.phases >= 3:
240  sensors.append(ATTR_PHASE3_VOLTAGE)
241  sensors.append(ATTR_PHASE3_POWER)
242  sensors.append(ATTR_PHASE3_CURRENT)
243 
244  entities.extend(
245  IskraSensor(coordinator, description)
246  for description in SENSOR_TYPES
247  if description.key in sensors
248  )
249 
250  if device.supports_counters:
251  for index, counter in enumerate(device.counters.non_resettable[:4]):
252  description = get_counter_entity_description(
253  counter, index, ATTR_NON_RESETTABLE_COUNTER
254  )
255  entities.append(IskraSensor(coordinator, description))
256 
257  for index, counter in enumerate(device.counters.resettable[:8]):
258  description = get_counter_entity_description(
259  counter, index, ATTR_RESETTABLE_COUNTER
260  )
261  entities.append(IskraSensor(coordinator, description))
262 
263  async_add_entities(entities)
264 
265 
267  """Representation of a Sensor."""
268 
269  entity_description: IskraSensorEntityDescription
270 
271  def __init__(
272  self,
273  coordinator: IskraDataUpdateCoordinator,
274  description: IskraSensorEntityDescription,
275  ) -> None:
276  """Initialize the sensor."""
277  super().__init__(coordinator)
278  self.entity_descriptionentity_description = description
279  self._attr_unique_id_attr_unique_id = f"{coordinator.device.serial}_{description.key}"
280 
281  @property
282  def native_value(self) -> float | None:
283  """Return the state of the sensor."""
284  return self.entity_descriptionentity_description.value_func(self.devicedevicedevice)
None __init__(self, IskraDataUpdateCoordinator coordinator, IskraSensorEntityDescription description)
Definition: sensor.py:275
IskraSensorEntityDescription get_counter_entity_description(Counter counter, int index, str entity_name)
Definition: sensor.py:174
None async_setup_entry(HomeAssistant hass, IskraConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:212