Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for monitoring a Sense energy sensor."""
2 
3 from datetime import datetime
4 
5 from sense_energy import ASyncSenseable, Scale
6 from sense_energy.sense_api import SenseDevice
7 
9  SensorDeviceClass,
10  SensorEntity,
11  SensorStateClass,
12 )
13 from homeassistant.const import (
14  PERCENTAGE,
15  UnitOfElectricPotential,
16  UnitOfEnergy,
17  UnitOfPower,
18 )
19 from homeassistant.core import HomeAssistant
20 from homeassistant.helpers.entity_platform import AddEntitiesCallback
21 
22 from . import SenseConfigEntry
23 from .const import (
24  ACTIVE_TYPE,
25  CONSUMPTION_ID,
26  CONSUMPTION_NAME,
27  FROM_GRID_ID,
28  FROM_GRID_NAME,
29  NET_PRODUCTION_ID,
30  NET_PRODUCTION_NAME,
31  PRODUCTION_ID,
32  PRODUCTION_NAME,
33  PRODUCTION_PCT_ID,
34  PRODUCTION_PCT_NAME,
35  SOLAR_POWERED_ID,
36  SOLAR_POWERED_NAME,
37  TO_GRID_ID,
38  TO_GRID_NAME,
39 )
40 from .coordinator import SenseRealtimeCoordinator, SenseTrendCoordinator
41 from .entity import SenseDeviceEntity, SenseEntity
42 
43 # Sensor types/ranges
44 TRENDS_SENSOR_TYPES = {
45  Scale.DAY: "Daily",
46  Scale.WEEK: "Weekly",
47  Scale.MONTH: "Monthly",
48  Scale.YEAR: "Yearly",
49  Scale.CYCLE: "Bill",
50 }
51 
52 # Production/consumption variants
53 SENSOR_VARIANTS = [(PRODUCTION_ID, PRODUCTION_NAME), (CONSUMPTION_ID, CONSUMPTION_NAME)]
54 
55 # Trend production/consumption variants
56 TREND_SENSOR_VARIANTS = [
57  *SENSOR_VARIANTS,
58  (PRODUCTION_PCT_ID, PRODUCTION_PCT_NAME),
59  (NET_PRODUCTION_ID, NET_PRODUCTION_NAME),
60  (FROM_GRID_ID, FROM_GRID_NAME),
61  (TO_GRID_ID, TO_GRID_NAME),
62  (SOLAR_POWERED_ID, SOLAR_POWERED_NAME),
63 ]
64 
65 
67  hass: HomeAssistant,
68  config_entry: SenseConfigEntry,
69  async_add_entities: AddEntitiesCallback,
70 ) -> None:
71  """Set up the Sense sensor."""
72  data = config_entry.runtime_data.data
73  trends_coordinator = config_entry.runtime_data.trends
74  realtime_coordinator = config_entry.runtime_data.rt
75 
76  # Request only in case it takes longer
77  # than 60s
78  await trends_coordinator.async_request_refresh()
79 
80  sense_monitor_id = data.sense_monitor_id
81 
82  entities: list[SensorEntity] = []
83 
84  for device in config_entry.runtime_data.data.devices:
85  entities.append(
86  SenseDevicePowerSensor(device, sense_monitor_id, realtime_coordinator)
87  )
88  entities.extend(
89  SenseDeviceEnergySensor(device, scale, trends_coordinator, sense_monitor_id)
90  for scale in Scale
91  )
92 
93  for variant_id, variant_name in SENSOR_VARIANTS:
94  entities.append(
96  data, sense_monitor_id, variant_id, variant_name, realtime_coordinator
97  )
98  )
99 
100  entities.extend(
101  SenseVoltageSensor(data, i, sense_monitor_id, realtime_coordinator)
102  for i in range(len(data.active_voltage))
103  )
104 
105  for scale in Scale:
106  for variant_id, variant_name in TREND_SENSOR_VARIANTS:
107  entities.append(
109  data,
110  scale,
111  variant_id,
112  variant_name,
113  trends_coordinator,
114  sense_monitor_id,
115  )
116  )
117 
118  async_add_entities(entities)
119 
120 
122  """Implementation of a Sense energy sensor."""
123 
124  _attr_device_class = SensorDeviceClass.POWER
125  _attr_native_unit_of_measurement = UnitOfPower.WATT
126  _attr_state_class = SensorStateClass.MEASUREMENT
127 
128  def __init__(
129  self,
130  gateway: ASyncSenseable,
131  sense_monitor_id: str,
132  variant_id: str,
133  variant_name: str,
134  realtime_coordinator: SenseRealtimeCoordinator,
135  ) -> None:
136  """Initialize the Sense sensor."""
137  super().__init__(
138  gateway,
139  realtime_coordinator,
140  sense_monitor_id,
141  f"{ACTIVE_TYPE}-{variant_id}",
142  )
143  self._attr_name_attr_name = variant_name
144  self._variant_id_variant_id = variant_id
145 
146  @property
147  def native_value(self) -> float:
148  """Return the state of the sensor."""
149  return round(
150  self._gateway_gateway_gateway.active_solar_power
151  if self._variant_id_variant_id == PRODUCTION_ID
152  else self._gateway_gateway_gateway.active_power
153  )
154 
155 
157  """Implementation of a Sense energy voltage sensor."""
158 
159  _attr_device_class = SensorDeviceClass.VOLTAGE
160  _attr_state_class = SensorStateClass.MEASUREMENT
161  _attr_native_unit_of_measurement = UnitOfElectricPotential.VOLT
162 
163  def __init__(
164  self,
165  gateway: ASyncSenseable,
166  index: int,
167  sense_monitor_id: str,
168  realtime_coordinator: SenseRealtimeCoordinator,
169  ) -> None:
170  """Initialize the Sense sensor."""
171  super().__init__(
172  gateway, realtime_coordinator, sense_monitor_id, f"L{index + 1}"
173  )
174  self._attr_name_attr_name = f"L{index + 1} Voltage"
175  self._voltage_index_voltage_index = index
176 
177  @property
178  def native_value(self) -> float:
179  """Return the state of the sensor."""
180  return round(self._gateway_gateway_gateway.active_voltage[self._voltage_index_voltage_index], 1)
181 
182 
184  """Implementation of a Sense energy sensor."""
185 
186  def __init__(
187  self,
188  gateway: ASyncSenseable,
189  scale: Scale,
190  variant_id: str,
191  variant_name: str,
192  trends_coordinator: SenseTrendCoordinator,
193  sense_monitor_id: str,
194  ) -> None:
195  """Initialize the Sense sensor."""
196  super().__init__(
197  gateway,
198  trends_coordinator,
199  sense_monitor_id,
200  f"{TRENDS_SENSOR_TYPES[scale].lower()}-{variant_id}",
201  )
202  self._attr_name_attr_name = f"{TRENDS_SENSOR_TYPES[scale]} {variant_name}"
203  self._scale_scale = scale
204  self._variant_id_variant_id = variant_id
205  self._had_any_update_had_any_update = False
206  if variant_id in [PRODUCTION_PCT_ID, SOLAR_POWERED_ID]:
207  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = PERCENTAGE
208  self._attr_entity_registry_enabled_default_attr_entity_registry_enabled_default = False
209  self._attr_state_class_attr_state_class = None
210  self._attr_device_class_attr_device_class = None
211  else:
212  self._attr_device_class_attr_device_class = SensorDeviceClass.ENERGY
213  self._attr_state_class_attr_state_class = SensorStateClass.TOTAL
214  self._attr_native_unit_of_measurement_attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
215 
216  @property
217  def native_value(self) -> float:
218  """Return the state of the sensor."""
219  return round(self._gateway_gateway_gateway.get_stat(self._scale_scale, self._variant_id_variant_id), 1)
220 
221  @property
222  def last_reset(self) -> datetime | None:
223  """Return the time when the sensor was last reset, if any."""
224  if self._attr_state_class_attr_state_class == SensorStateClass.TOTAL:
225  return self._gateway_gateway_gateway.trend_start(self._scale_scale)
226  return None
227 
228 
230  """Implementation of a Sense energy device."""
231 
232  _attr_state_class = SensorStateClass.MEASUREMENT
233  _attr_native_unit_of_measurement = UnitOfPower.WATT
234  _attr_device_class = SensorDeviceClass.POWER
235 
236  def __init__(
237  self,
238  device: SenseDevice,
239  sense_monitor_id: str,
240  coordinator: SenseRealtimeCoordinator,
241  ) -> None:
242  """Initialize the Sense device sensor."""
243  super().__init__(
244  device, coordinator, sense_monitor_id, f"{device.id}-{CONSUMPTION_ID}"
245  )
246 
247  @property
248  def native_value(self) -> float:
249  """Return the state of the sensor."""
250  return self._device_device.power_w
251 
252 
254  """Implementation of a Sense device energy sensor."""
255 
256  _attr_native_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR
257  _attr_state_class = SensorStateClass.TOTAL_INCREASING
258  _attr_device_class = SensorDeviceClass.ENERGY
259 
260  def __init__(
261  self,
262  device: SenseDevice,
263  scale: Scale,
264  coordinator: SenseTrendCoordinator,
265  sense_monitor_id: str,
266  ) -> None:
267  """Initialize the Sense device sensor."""
268  super().__init__(
269  device,
270  coordinator,
271  sense_monitor_id,
272  f"{device.id}-{TRENDS_SENSOR_TYPES[scale].lower()}-energy",
273  )
274  self._attr_translation_key_attr_translation_key = f"{TRENDS_SENSOR_TYPES[scale].lower()}_energy"
275  self._attr_suggested_display_precision_attr_suggested_display_precision = 2
276  self._scale_scale = scale
277  self._device_device_device = device
278 
279  @property
280  def native_value(self) -> float:
281  """Return the state of the sensor."""
282  return self._device_device_device.energy_kwh[self._scale_scale]
None __init__(self, SenseDevice device, Scale scale, SenseTrendCoordinator coordinator, str sense_monitor_id)
Definition: sensor.py:266
None __init__(self, SenseDevice device, str sense_monitor_id, SenseRealtimeCoordinator coordinator)
Definition: sensor.py:241
None __init__(self, ASyncSenseable gateway, str sense_monitor_id, str variant_id, str variant_name, SenseRealtimeCoordinator realtime_coordinator)
Definition: sensor.py:135
None __init__(self, ASyncSenseable gateway, Scale scale, str variant_id, str variant_name, SenseTrendCoordinator trends_coordinator, str sense_monitor_id)
Definition: sensor.py:194
None __init__(self, ASyncSenseable gateway, int index, str sense_monitor_id, SenseRealtimeCoordinator realtime_coordinator)
Definition: sensor.py:169
None async_setup_entry(HomeAssistant hass, SenseConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:70