Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Discovergy sensor entity."""
2 
3 from collections.abc import Callable
4 from dataclasses import dataclass, field
5 from datetime import datetime
6 
7 from pydiscovergy.models import Reading
8 
10  SensorDeviceClass,
11  SensorEntity,
12  SensorEntityDescription,
13  SensorStateClass,
14 )
15 from homeassistant.const import (
16  EntityCategory,
17  UnitOfElectricPotential,
18  UnitOfEnergy,
19  UnitOfPower,
20  UnitOfVolume,
21 )
22 from homeassistant.core import HomeAssistant
23 from homeassistant.helpers.device_registry import DeviceInfo
24 from homeassistant.helpers.entity_platform import AddEntitiesCallback
25 from homeassistant.helpers.update_coordinator import CoordinatorEntity
26 
27 from . import DiscovergyConfigEntry
28 from .const import DOMAIN, MANUFACTURER
29 from .coordinator import DiscovergyUpdateCoordinator
30 
31 
32 def _get_and_scale(reading: Reading, key: str, scale: int) -> datetime | float | None:
33  """Get a value from a Reading and divide with scale it."""
34  if (value := reading.values.get(key)) is not None:
35  return value / scale
36  return None
37 
38 
39 @dataclass(frozen=True, kw_only=True)
41  """Class to describe a Discovergy sensor entity."""
42 
43  value_fn: Callable[[Reading, str, int], datetime | float | None] = field(
44  default=_get_and_scale
45  )
46  alternative_keys: list[str] = field(default_factory=list)
47  scale: int = field(default_factory=lambda: 1000)
48 
49 
50 GAS_SENSORS: tuple[DiscovergySensorEntityDescription, ...] = (
52  key="volume",
53  translation_key="total_gas_consumption",
54  suggested_display_precision=4,
55  native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
56  device_class=SensorDeviceClass.GAS,
57  state_class=SensorStateClass.TOTAL_INCREASING,
58  ),
59 )
60 
61 ELECTRICITY_SENSORS: tuple[DiscovergySensorEntityDescription, ...] = (
62  # power sensors
64  key="power",
65  translation_key="total_power",
66  native_unit_of_measurement=UnitOfPower.WATT,
67  suggested_display_precision=3,
68  device_class=SensorDeviceClass.POWER,
69  state_class=SensorStateClass.MEASUREMENT,
70  ),
72  key="power1",
73  translation_key="phase_1_power",
74  native_unit_of_measurement=UnitOfPower.WATT,
75  suggested_display_precision=3,
76  device_class=SensorDeviceClass.POWER,
77  state_class=SensorStateClass.MEASUREMENT,
78  entity_registry_enabled_default=False,
79  alternative_keys=["phase1Power"],
80  ),
82  key="power2",
83  translation_key="phase_2_power",
84  native_unit_of_measurement=UnitOfPower.WATT,
85  suggested_display_precision=3,
86  device_class=SensorDeviceClass.POWER,
87  state_class=SensorStateClass.MEASUREMENT,
88  entity_registry_enabled_default=False,
89  alternative_keys=["phase2Power"],
90  ),
92  key="power3",
93  translation_key="phase_3_power",
94  native_unit_of_measurement=UnitOfPower.WATT,
95  suggested_display_precision=3,
96  device_class=SensorDeviceClass.POWER,
97  state_class=SensorStateClass.MEASUREMENT,
98  entity_registry_enabled_default=False,
99  alternative_keys=["phase3Power"],
100  ),
101  # voltage sensors
103  key="phase1Voltage",
104  translation_key="phase_1_voltage",
105  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
106  suggested_display_precision=1,
107  device_class=SensorDeviceClass.VOLTAGE,
108  state_class=SensorStateClass.MEASUREMENT,
109  entity_registry_enabled_default=False,
110  alternative_keys=["voltage1"],
111  ),
113  key="phase2Voltage",
114  translation_key="phase_2_voltage",
115  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
116  suggested_display_precision=1,
117  device_class=SensorDeviceClass.VOLTAGE,
118  state_class=SensorStateClass.MEASUREMENT,
119  entity_registry_enabled_default=False,
120  alternative_keys=["voltage2"],
121  ),
123  key="phase3Voltage",
124  translation_key="phase_3_voltage",
125  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
126  suggested_display_precision=1,
127  device_class=SensorDeviceClass.VOLTAGE,
128  state_class=SensorStateClass.MEASUREMENT,
129  entity_registry_enabled_default=False,
130  alternative_keys=["voltage3"],
131  ),
132  # energy sensors
134  key="energy",
135  translation_key="total_consumption",
136  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
137  suggested_display_precision=4,
138  device_class=SensorDeviceClass.ENERGY,
139  state_class=SensorStateClass.TOTAL_INCREASING,
140  scale=10000000000,
141  ),
143  key="energyOut",
144  translation_key="total_production",
145  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
146  suggested_display_precision=4,
147  device_class=SensorDeviceClass.ENERGY,
148  state_class=SensorStateClass.TOTAL_INCREASING,
149  scale=10000000000,
150  ),
151 )
152 
153 ADDITIONAL_SENSORS: tuple[DiscovergySensorEntityDescription, ...] = (
155  key="last_transmitted",
156  translation_key="last_transmitted",
157  device_class=SensorDeviceClass.TIMESTAMP,
158  entity_category=EntityCategory.DIAGNOSTIC,
159  entity_registry_enabled_default=False,
160  value_fn=lambda reading, key, scale: reading.time,
161  ),
162 )
163 
164 
166  hass: HomeAssistant,
167  entry: DiscovergyConfigEntry,
168  async_add_entities: AddEntitiesCallback,
169 ) -> None:
170  """Set up the Discovergy sensors."""
171  entities: list[DiscovergySensor] = []
172  for coordinator in entry.runtime_data:
173  sensors: tuple[DiscovergySensorEntityDescription, ...] = ()
174 
175  # select sensor descriptions based on meter type and combine with additional sensors
176  if coordinator.meter.measurement_type == "ELECTRICITY":
177  sensors = ELECTRICITY_SENSORS + ADDITIONAL_SENSORS
178  elif coordinator.meter.measurement_type == "GAS":
179  sensors = GAS_SENSORS + ADDITIONAL_SENSORS
180 
181  entities.extend(
182  DiscovergySensor(value_key, description, coordinator)
183  for description in sensors
184  for value_key in {description.key, *description.alternative_keys}
185  if description.value_fn(coordinator.data, value_key, description.scale)
186  is not None
187  )
188 
189  async_add_entities(entities)
190 
191 
192 class DiscovergySensor(CoordinatorEntity[DiscovergyUpdateCoordinator], SensorEntity):
193  """Represents a Discovergy smart meter sensor."""
194 
195  entity_description: DiscovergySensorEntityDescription
196  data_key: str
197  _attr_has_entity_name = True
198 
199  def __init__(
200  self,
201  data_key: str,
202  description: DiscovergySensorEntityDescription,
203  coordinator: DiscovergyUpdateCoordinator,
204  ) -> None:
205  """Initialize the sensor."""
206  super().__init__(coordinator)
207 
208  self.data_keydata_key = data_key
209  self.entity_descriptionentity_description = description
210 
211  meter = coordinator.meter
212  self._attr_unique_id_attr_unique_id = f"{meter.full_serial_number}-{data_key}"
213  self._attr_device_info_attr_device_info = DeviceInfo(
214  identifiers={(DOMAIN, meter.meter_id)},
215  name=f"{meter.measurement_type.capitalize()} {meter.location.street} {meter.location.street_number}",
216  model=meter.meter_type,
217  manufacturer=MANUFACTURER,
218  serial_number=meter.full_serial_number,
219  )
220 
221  @property
222  def native_value(self) -> datetime | float | None:
223  """Return the sensor state."""
224  return self.entity_descriptionentity_description.value_fn(
225  self.coordinator.data, self.data_keydata_key, self.entity_descriptionentity_description.scale
226  )
None __init__(self, str data_key, DiscovergySensorEntityDescription description, DiscovergyUpdateCoordinator coordinator)
Definition: sensor.py:204
datetime|float|None _get_and_scale(Reading reading, str key, int scale)
Definition: sensor.py:32
None async_setup_entry(HomeAssistant hass, DiscovergyConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:169