Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor platform for Tessie integration."""
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 itertools import chain
9 from typing import cast
10 
12  SensorDeviceClass,
13  SensorEntity,
14  SensorEntityDescription,
15  SensorStateClass,
16 )
17 from homeassistant.const import (
18  PERCENTAGE,
19  EntityCategory,
20  UnitOfElectricCurrent,
21  UnitOfElectricPotential,
22  UnitOfEnergy,
23  UnitOfLength,
24  UnitOfPower,
25  UnitOfPressure,
26  UnitOfSpeed,
27  UnitOfTemperature,
28  UnitOfTime,
29 )
30 from homeassistant.core import HomeAssistant, callback
31 from homeassistant.helpers.entity_platform import AddEntitiesCallback
32 from homeassistant.helpers.typing import StateType
33 from homeassistant.util import dt as dt_util
34 from homeassistant.util.variance import ignore_variance
35 
36 from . import TessieConfigEntry
37 from .const import TessieChargeStates, TessieWallConnectorStates
38 from .entity import TessieEnergyEntity, TessieEntity, TessieWallConnectorEntity
39 from .models import TessieEnergyData, TessieVehicleData
40 
41 
42 @callback
43 def minutes_to_datetime(value: StateType) -> datetime | None:
44  """Convert relative minutes into absolute datetime."""
45  if isinstance(value, (int, float)) and value > 0:
46  return dt_util.now() + timedelta(minutes=value)
47  return None
48 
49 
50 @dataclass(frozen=True, kw_only=True)
52  """Describes Tessie Sensor entity."""
53 
54  value_fn: Callable[[StateType], StateType | datetime] = lambda x: x
55  available_fn: Callable[[StateType], bool] = lambda _: True
56 
57 
58 DESCRIPTIONS: tuple[TessieSensorEntityDescription, ...] = (
60  key="charge_state_charging_state",
61  options=list(TessieChargeStates.values()),
62  device_class=SensorDeviceClass.ENUM,
63  value_fn=lambda value: TessieChargeStates[cast(str, value)],
64  ),
66  key="charge_state_usable_battery_level",
67  state_class=SensorStateClass.MEASUREMENT,
68  native_unit_of_measurement=PERCENTAGE,
69  device_class=SensorDeviceClass.BATTERY,
70  ),
72  key="charge_state_charge_energy_added",
73  state_class=SensorStateClass.TOTAL_INCREASING,
74  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
75  device_class=SensorDeviceClass.ENERGY,
76  suggested_display_precision=1,
77  ),
79  key="charge_state_charger_power",
80  state_class=SensorStateClass.MEASUREMENT,
81  native_unit_of_measurement=UnitOfPower.KILO_WATT,
82  device_class=SensorDeviceClass.POWER,
83  ),
85  key="charge_state_charger_voltage",
86  state_class=SensorStateClass.MEASUREMENT,
87  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
88  device_class=SensorDeviceClass.VOLTAGE,
89  entity_category=EntityCategory.DIAGNOSTIC,
90  ),
92  key="charge_state_charger_actual_current",
93  state_class=SensorStateClass.MEASUREMENT,
94  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
95  device_class=SensorDeviceClass.CURRENT,
96  entity_category=EntityCategory.DIAGNOSTIC,
97  ),
99  key="charge_state_charge_rate",
100  state_class=SensorStateClass.MEASUREMENT,
101  native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
102  device_class=SensorDeviceClass.SPEED,
103  entity_category=EntityCategory.DIAGNOSTIC,
104  ),
106  key="charge_state_minutes_to_full_charge",
107  device_class=SensorDeviceClass.TIMESTAMP,
108  entity_category=EntityCategory.DIAGNOSTIC,
109  value_fn=minutes_to_datetime,
110  ),
112  key="charge_state_battery_range",
113  state_class=SensorStateClass.MEASUREMENT,
114  native_unit_of_measurement=UnitOfLength.MILES,
115  device_class=SensorDeviceClass.DISTANCE,
116  suggested_display_precision=1,
117  ),
119  key="charge_state_est_battery_range",
120  state_class=SensorStateClass.MEASUREMENT,
121  native_unit_of_measurement=UnitOfLength.MILES,
122  device_class=SensorDeviceClass.DISTANCE,
123  suggested_display_precision=1,
124  entity_registry_enabled_default=False,
125  ),
127  key="charge_state_ideal_battery_range",
128  state_class=SensorStateClass.MEASUREMENT,
129  native_unit_of_measurement=UnitOfLength.MILES,
130  device_class=SensorDeviceClass.DISTANCE,
131  suggested_display_precision=1,
132  entity_registry_enabled_default=False,
133  ),
135  key="drive_state_speed",
136  state_class=SensorStateClass.MEASUREMENT,
137  native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
138  device_class=SensorDeviceClass.SPEED,
139  ),
141  key="drive_state_power",
142  state_class=SensorStateClass.MEASUREMENT,
143  native_unit_of_measurement=UnitOfPower.KILO_WATT,
144  device_class=SensorDeviceClass.POWER,
145  entity_category=EntityCategory.DIAGNOSTIC,
146  ),
148  key="drive_state_shift_state",
149  options=["p", "d", "r", "n"],
150  device_class=SensorDeviceClass.ENUM,
151  value_fn=lambda x: x.lower() if isinstance(x, str) else x,
152  ),
154  key="vehicle_state_odometer",
155  state_class=SensorStateClass.TOTAL_INCREASING,
156  native_unit_of_measurement=UnitOfLength.MILES,
157  device_class=SensorDeviceClass.DISTANCE,
158  suggested_display_precision=0,
159  entity_category=EntityCategory.DIAGNOSTIC,
160  ),
162  key="vehicle_state_tpms_pressure_fl",
163  state_class=SensorStateClass.MEASUREMENT,
164  native_unit_of_measurement=UnitOfPressure.BAR,
165  suggested_unit_of_measurement=UnitOfPressure.PSI,
166  device_class=SensorDeviceClass.PRESSURE,
167  suggested_display_precision=1,
168  entity_category=EntityCategory.DIAGNOSTIC,
169  ),
171  key="vehicle_state_tpms_pressure_fr",
172  state_class=SensorStateClass.MEASUREMENT,
173  native_unit_of_measurement=UnitOfPressure.BAR,
174  suggested_unit_of_measurement=UnitOfPressure.PSI,
175  device_class=SensorDeviceClass.PRESSURE,
176  suggested_display_precision=1,
177  entity_category=EntityCategory.DIAGNOSTIC,
178  ),
180  key="vehicle_state_tpms_pressure_rl",
181  state_class=SensorStateClass.MEASUREMENT,
182  native_unit_of_measurement=UnitOfPressure.BAR,
183  suggested_unit_of_measurement=UnitOfPressure.PSI,
184  device_class=SensorDeviceClass.PRESSURE,
185  suggested_display_precision=1,
186  entity_category=EntityCategory.DIAGNOSTIC,
187  ),
189  key="vehicle_state_tpms_pressure_rr",
190  state_class=SensorStateClass.MEASUREMENT,
191  native_unit_of_measurement=UnitOfPressure.BAR,
192  suggested_unit_of_measurement=UnitOfPressure.PSI,
193  device_class=SensorDeviceClass.PRESSURE,
194  suggested_display_precision=1,
195  entity_category=EntityCategory.DIAGNOSTIC,
196  ),
198  key="climate_state_inside_temp",
199  state_class=SensorStateClass.MEASUREMENT,
200  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
201  device_class=SensorDeviceClass.TEMPERATURE,
202  suggested_display_precision=1,
203  ),
205  key="climate_state_outside_temp",
206  state_class=SensorStateClass.MEASUREMENT,
207  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
208  device_class=SensorDeviceClass.TEMPERATURE,
209  suggested_display_precision=1,
210  ),
212  key="climate_state_driver_temp_setting",
213  state_class=SensorStateClass.MEASUREMENT,
214  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
215  device_class=SensorDeviceClass.TEMPERATURE,
216  suggested_display_precision=1,
217  entity_category=EntityCategory.DIAGNOSTIC,
218  ),
220  key="climate_state_passenger_temp_setting",
221  state_class=SensorStateClass.MEASUREMENT,
222  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
223  device_class=SensorDeviceClass.TEMPERATURE,
224  suggested_display_precision=1,
225  entity_category=EntityCategory.DIAGNOSTIC,
226  ),
228  key="drive_state_active_route_traffic_minutes_delay",
229  state_class=SensorStateClass.MEASUREMENT,
230  native_unit_of_measurement=UnitOfTime.MINUTES,
231  device_class=SensorDeviceClass.DURATION,
232  ),
234  key="drive_state_active_route_energy_at_arrival",
235  state_class=SensorStateClass.MEASUREMENT,
236  native_unit_of_measurement=PERCENTAGE,
237  device_class=SensorDeviceClass.BATTERY,
238  entity_category=EntityCategory.DIAGNOSTIC,
239  ),
241  key="drive_state_active_route_miles_to_arrival",
242  state_class=SensorStateClass.MEASUREMENT,
243  native_unit_of_measurement=UnitOfLength.MILES,
244  device_class=SensorDeviceClass.DISTANCE,
245  ),
247  key="drive_state_active_route_minutes_to_arrival",
248  device_class=SensorDeviceClass.TIMESTAMP,
249  value_fn=ignore_variance(
250  lambda value: dt_util.now() + timedelta(minutes=cast(float, value)),
251  timedelta(seconds=30),
252  ),
253  available_fn=lambda x: x is not None,
254  ),
256  key="drive_state_active_route_destination",
257  entity_category=EntityCategory.DIAGNOSTIC,
258  ),
259 )
260 
261 ENERGY_LIVE_DESCRIPTIONS: tuple[TessieSensorEntityDescription, ...] = (
263  key="solar_power",
264  state_class=SensorStateClass.MEASUREMENT,
265  native_unit_of_measurement=UnitOfPower.WATT,
266  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
267  suggested_display_precision=2,
268  device_class=SensorDeviceClass.POWER,
269  ),
271  key="energy_left",
272  state_class=SensorStateClass.MEASUREMENT,
273  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
274  suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
275  suggested_display_precision=2,
276  device_class=SensorDeviceClass.ENERGY_STORAGE,
277  entity_category=EntityCategory.DIAGNOSTIC,
278  ),
280  key="total_pack_energy",
281  state_class=SensorStateClass.MEASUREMENT,
282  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
283  suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
284  suggested_display_precision=2,
285  device_class=SensorDeviceClass.ENERGY_STORAGE,
286  entity_category=EntityCategory.DIAGNOSTIC,
287  entity_registry_enabled_default=False,
288  ),
290  key="percentage_charged",
291  state_class=SensorStateClass.MEASUREMENT,
292  native_unit_of_measurement=PERCENTAGE,
293  device_class=SensorDeviceClass.BATTERY,
294  suggested_display_precision=2,
295  ),
297  key="battery_power",
298  state_class=SensorStateClass.MEASUREMENT,
299  native_unit_of_measurement=UnitOfPower.WATT,
300  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
301  suggested_display_precision=2,
302  device_class=SensorDeviceClass.POWER,
303  ),
305  key="load_power",
306  state_class=SensorStateClass.MEASUREMENT,
307  native_unit_of_measurement=UnitOfPower.WATT,
308  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
309  suggested_display_precision=2,
310  device_class=SensorDeviceClass.POWER,
311  ),
313  key="grid_power",
314  state_class=SensorStateClass.MEASUREMENT,
315  native_unit_of_measurement=UnitOfPower.WATT,
316  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
317  suggested_display_precision=2,
318  device_class=SensorDeviceClass.POWER,
319  ),
321  key="grid_services_power",
322  state_class=SensorStateClass.MEASUREMENT,
323  native_unit_of_measurement=UnitOfPower.WATT,
324  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
325  suggested_display_precision=2,
326  device_class=SensorDeviceClass.POWER,
327  ),
329  key="generator_power",
330  state_class=SensorStateClass.MEASUREMENT,
331  native_unit_of_measurement=UnitOfPower.WATT,
332  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
333  suggested_display_precision=2,
334  device_class=SensorDeviceClass.POWER,
335  entity_registry_enabled_default=False,
336  ),
337 )
338 
339 WALL_CONNECTOR_DESCRIPTIONS: tuple[TessieSensorEntityDescription, ...] = (
341  key="wall_connector_state",
342  entity_category=EntityCategory.DIAGNOSTIC,
343  entity_registry_enabled_default=False,
344  device_class=SensorDeviceClass.ENUM,
345  value_fn=lambda x: TessieWallConnectorStates(cast(int, x)).name.lower(),
346  options=[state.name.lower() for state in TessieWallConnectorStates],
347  ),
349  key="wall_connector_power",
350  state_class=SensorStateClass.MEASUREMENT,
351  native_unit_of_measurement=UnitOfPower.WATT,
352  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
353  suggested_display_precision=2,
354  device_class=SensorDeviceClass.POWER,
355  ),
357  key="vin",
358  ),
359 )
360 
361 ENERGY_INFO_DESCRIPTIONS: tuple[TessieSensorEntityDescription, ...] = (
363  key="vpp_backup_reserve_percent",
364  entity_category=EntityCategory.DIAGNOSTIC,
365  device_class=SensorDeviceClass.BATTERY,
366  native_unit_of_measurement=PERCENTAGE,
367  ),
368 )
369 
370 PARALLEL_UPDATES = 0
371 
372 
374  hass: HomeAssistant,
375  entry: TessieConfigEntry,
376  async_add_entities: AddEntitiesCallback,
377 ) -> None:
378  """Set up the Tessie sensor platform from a config entry."""
379 
381  chain(
382  ( # Add vehicles
383  TessieVehicleSensorEntity(vehicle, description)
384  for vehicle in entry.runtime_data.vehicles
385  for description in DESCRIPTIONS
386  ),
387  ( # Add energy site info
388  TessieEnergyInfoSensorEntity(energysite, description)
389  for energysite in entry.runtime_data.energysites
390  for description in ENERGY_INFO_DESCRIPTIONS
391  if description.key in energysite.info_coordinator.data
392  ),
393  ( # Add energy site live
394  TessieEnergyLiveSensorEntity(energysite, description)
395  for energysite in entry.runtime_data.energysites
396  for description in ENERGY_LIVE_DESCRIPTIONS
397  if description.key in energysite.live_coordinator.data
398  ),
399  ( # Add wall connectors
400  TessieWallConnectorSensorEntity(energysite, din, description)
401  for energysite in entry.runtime_data.energysites
402  for din in energysite.live_coordinator.data.get("wall_connectors", {})
403  for description in WALL_CONNECTOR_DESCRIPTIONS
404  ),
405  )
406  )
407 
408 
410  """Base class for Tessie sensor entities."""
411 
412  entity_description: TessieSensorEntityDescription
413 
414  def __init__(
415  self,
416  vehicle: TessieVehicleData,
417  description: TessieSensorEntityDescription,
418  ) -> None:
419  """Initialize the sensor."""
420  self.entity_descriptionentity_description = description
421  super().__init__(vehicle, description.key)
422 
423  @property
424  def native_value(self) -> StateType | datetime:
425  """Return the state of the sensor."""
426  return self.entity_descriptionentity_description.value_fn(self.getget())
427 
428  @property
429  def available(self) -> bool:
430  """Return if sensor is available."""
431  return super().available and self.entity_descriptionentity_description.available_fn(self.getget())
432 
433 
435  """Base class for Tessie energy site sensor entity."""
436 
437  entity_description: TessieSensorEntityDescription
438 
439  def __init__(
440  self,
441  data: TessieEnergyData,
442  description: TessieSensorEntityDescription,
443  ) -> None:
444  """Initialize the sensor."""
445  self.entity_descriptionentity_description = description
446  super().__init__(data, data.live_coordinator, description.key)
447 
448  def _async_update_attrs(self) -> None:
449  """Update the attributes of the sensor."""
450  self._attr_available_attr_available = self._value_value is not None
451  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self._value_value)
452 
453 
455  """Base class for Tessie energy site sensor entity."""
456 
457  entity_description: TessieSensorEntityDescription
458 
459  def __init__(
460  self,
461  data: TessieEnergyData,
462  description: TessieSensorEntityDescription,
463  ) -> None:
464  """Initialize the sensor."""
465  self.entity_descriptionentity_description = description
466  super().__init__(data, data.info_coordinator, description.key)
467 
468  def _async_update_attrs(self) -> None:
469  """Update the attributes of the sensor."""
470  self._attr_available_attr_available = self._value_value is not None
471  self._attr_native_value_attr_native_value = self._value_value
472 
473 
475  """Base class for Tessie wall connector sensor entity."""
476 
477  entity_description: TessieSensorEntityDescription
478 
479  def __init__(
480  self,
481  data: TessieEnergyData,
482  din: str,
483  description: TessieSensorEntityDescription,
484  ) -> None:
485  """Initialize the sensor."""
486  self.entity_descriptionentity_description = description
487  super().__init__(
488  data,
489  din,
490  description.key,
491  )
492 
493  def _async_update_attrs(self) -> None:
494  """Update the attributes of the sensor."""
495  self._attr_available_attr_available = self._value_value_value is not None
496  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self._value_value_value)
Any get(self, str|None key=None, Any|None default=None)
Definition: entity.py:52
None __init__(self, TessieEnergyData data, TessieSensorEntityDescription description)
Definition: sensor.py:463
None __init__(self, TessieEnergyData data, TessieSensorEntityDescription description)
Definition: sensor.py:443
None __init__(self, TessieVehicleData vehicle, TessieSensorEntityDescription description)
Definition: sensor.py:418
None __init__(self, TessieEnergyData data, str din, TessieSensorEntityDescription description)
Definition: sensor.py:484
None async_setup_entry(HomeAssistant hass, TessieConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:377
datetime|None minutes_to_datetime(StateType value)
Definition: sensor.py:43