Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for IoTaWatt Energy monitor."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 import logging
8 
9 from iotawattpy.sensor import Sensor
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15  SensorStateClass,
16 )
17 from homeassistant.config_entries import ConfigEntry
18 from homeassistant.const import (
19  PERCENTAGE,
20  UnitOfApparentPower,
21  UnitOfElectricCurrent,
22  UnitOfElectricPotential,
23  UnitOfEnergy,
24  UnitOfFrequency,
25  UnitOfPower,
26 )
27 from homeassistant.core import HomeAssistant, callback
28 from homeassistant.helpers import device_registry as dr, entity_registry as er
29 from homeassistant.helpers.entity_platform import AddEntitiesCallback
30 from homeassistant.helpers.typing import StateType
31 from homeassistant.helpers.update_coordinator import CoordinatorEntity
32 from homeassistant.util import dt as dt_util
33 
34 from .const import DOMAIN, VOLT_AMPERE_REACTIVE, VOLT_AMPERE_REACTIVE_HOURS
35 from .coordinator import IotawattUpdater
36 
37 _LOGGER = logging.getLogger(__name__)
38 
39 
40 @dataclass(frozen=True)
42  """Class describing IotaWatt sensor entities."""
43 
44  value: Callable | None = None
45 
46 
47 ENTITY_DESCRIPTION_KEY_MAP: dict[str, IotaWattSensorEntityDescription] = {
49  key="Amps",
50  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
51  state_class=SensorStateClass.MEASUREMENT,
52  device_class=SensorDeviceClass.CURRENT,
53  entity_registry_enabled_default=False,
54  ),
56  key="Hz",
57  native_unit_of_measurement=UnitOfFrequency.HERTZ,
58  state_class=SensorStateClass.MEASUREMENT,
59  device_class=SensorDeviceClass.FREQUENCY,
60  icon="mdi:flash",
61  entity_registry_enabled_default=False,
62  ),
64  key="PF",
65  native_unit_of_measurement=PERCENTAGE,
66  state_class=SensorStateClass.MEASUREMENT,
67  device_class=SensorDeviceClass.POWER_FACTOR,
68  value=lambda value: value * 100,
69  entity_registry_enabled_default=False,
70  ),
72  key="Watts",
73  native_unit_of_measurement=UnitOfPower.WATT,
74  state_class=SensorStateClass.MEASUREMENT,
75  device_class=SensorDeviceClass.POWER,
76  ),
78  key="WattHours",
79  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
80  state_class=SensorStateClass.TOTAL,
81  device_class=SensorDeviceClass.ENERGY,
82  ),
84  key="VA",
85  native_unit_of_measurement=UnitOfApparentPower.VOLT_AMPERE,
86  state_class=SensorStateClass.MEASUREMENT,
87  device_class=SensorDeviceClass.APPARENT_POWER,
88  entity_registry_enabled_default=False,
89  ),
91  key="VAR",
92  native_unit_of_measurement=VOLT_AMPERE_REACTIVE,
93  state_class=SensorStateClass.MEASUREMENT,
94  icon="mdi:flash",
95  entity_registry_enabled_default=False,
96  ),
98  key="VARh",
99  native_unit_of_measurement=VOLT_AMPERE_REACTIVE_HOURS,
100  state_class=SensorStateClass.MEASUREMENT,
101  icon="mdi:flash",
102  entity_registry_enabled_default=False,
103  ),
105  key="Volts",
106  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
107  state_class=SensorStateClass.MEASUREMENT,
108  device_class=SensorDeviceClass.VOLTAGE,
109  entity_registry_enabled_default=False,
110  ),
111 }
112 
113 
115  hass: HomeAssistant,
116  config_entry: ConfigEntry,
117  async_add_entities: AddEntitiesCallback,
118 ) -> None:
119  """Add sensors for passed config_entry in HA."""
120  coordinator: IotawattUpdater = hass.data[DOMAIN][config_entry.entry_id]
121  created = set()
122 
123  @callback
124  def _create_entity(key: str) -> IotaWattSensor:
125  """Create a sensor entity."""
126  created.add(key)
127  data = coordinator.data["sensors"][key]
128  description = ENTITY_DESCRIPTION_KEY_MAP.get(
129  data.getUnit(), IotaWattSensorEntityDescription(key="base_sensor")
130  )
131 
132  return IotaWattSensor(
133  coordinator=coordinator,
134  key=key,
135  entity_description=description,
136  )
137 
138  async_add_entities(_create_entity(key) for key in coordinator.data["sensors"])
139 
140  @callback
141  def new_data_received():
142  """Check for new sensors."""
143  entities = [
144  _create_entity(key)
145  for key in coordinator.data["sensors"]
146  if key not in created
147  ]
148  async_add_entities(entities)
149 
150  coordinator.async_add_listener(new_data_received)
151 
152 
153 class IotaWattSensor(CoordinatorEntity[IotawattUpdater], SensorEntity):
154  """Defines a IoTaWatt Energy Sensor."""
155 
156  entity_description: IotaWattSensorEntityDescription
157 
158  def __init__(
159  self,
160  coordinator: IotawattUpdater,
161  key: str,
162  entity_description: IotaWattSensorEntityDescription,
163  ) -> None:
164  """Initialize the sensor."""
165  super().__init__(coordinator=coordinator)
166 
167  self._key_key = key
168  data = self._sensor_data_sensor_data
169  if data.getType() == "Input":
170  self._attr_unique_id_attr_unique_id = (
171  f"{data.hub_mac_address}-input-{data.getChannel()}-{data.getUnit()}"
172  )
173  self.entity_descriptionentity_description = entity_description
174 
175  @property
176  def _sensor_data(self) -> Sensor:
177  """Return sensor data."""
178  return self.coordinator.data["sensors"][self._key_key]
179 
180  @property
181  def name(self) -> str | None:
182  """Return name of the entity."""
183  return self._sensor_data_sensor_data.getName()
184 
185  @property
186  def device_info(self) -> dr.DeviceInfo:
187  """Return device info."""
188  return dr.DeviceInfo(
189  connections={
190  (dr.CONNECTION_NETWORK_MAC, self._sensor_data_sensor_data.hub_mac_address)
191  },
192  manufacturer="IoTaWatt",
193  model="IoTaWatt",
194  )
195 
196  @callback
197  def _handle_coordinator_update(self) -> None:
198  """Handle updated data from the coordinator."""
199  if self._key_key not in self.coordinator.data["sensors"]:
200  if self._attr_unique_id_attr_unique_id:
201  er.async_get(self.hasshasshass).async_remove(self.entity_identity_id)
202  else:
203  self.hasshasshass.async_create_task(self.async_removeasync_remove())
204  return
205 
206  if (begin := self._sensor_data_sensor_data.getBegin()) and (
207  last_reset := dt_util.parse_datetime(begin)
208  ):
209  self._attr_last_reset_attr_last_reset = last_reset
210 
212 
213  @property
214  def extra_state_attributes(self) -> dict[str, str]:
215  """Return the extra state attributes of the entity."""
216  data = self._sensor_data_sensor_data
217  attrs = {"type": data.getType()}
218  if attrs["type"] == "Input":
219  attrs["channel"] = data.getChannel()
220 
221  return attrs
222 
223  @property
224  def native_value(self) -> StateType:
225  """Return the state of the sensor."""
226  if func := self.entity_descriptionentity_description.value:
227  return func(self._sensor_data_sensor_data.getValue())
228 
229  return self._sensor_data_sensor_data.getValue()
None __init__(self, IotawattUpdater coordinator, str key, IotaWattSensorEntityDescription entity_description)
Definition: sensor.py:163
None async_remove(self, *bool force_remove=False)
Definition: entity.py:1387
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:118