Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Sensor platform for Teslemetry integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from datetime import 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
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 TeslemetryConfigEntry
37 from .const import ENERGY_HISTORY_FIELDS
38 from .entity import (
39  TeslemetryEnergyHistoryEntity,
40  TeslemetryEnergyInfoEntity,
41  TeslemetryEnergyLiveEntity,
42  TeslemetryVehicleEntity,
43  TeslemetryWallConnectorEntity,
44 )
45 from .models import TeslemetryEnergyData, TeslemetryVehicleData
46 
47 PARALLEL_UPDATES = 0
48 
49 CHARGE_STATES = {
50  "Starting": "starting",
51  "Charging": "charging",
52  "Stopped": "stopped",
53  "Complete": "complete",
54  "Disconnected": "disconnected",
55  "NoPower": "no_power",
56 }
57 
58 SHIFT_STATES = {"P": "p", "D": "d", "R": "r", "N": "n"}
59 
60 
61 @dataclass(frozen=True, kw_only=True)
63  """Describes Teslemetry Sensor entity."""
64 
65  value_fn: Callable[[StateType], StateType] = lambda x: x
66 
67 
68 VEHICLE_DESCRIPTIONS: tuple[TeslemetrySensorEntityDescription, ...] = (
70  key="charge_state_charging_state",
71  options=list(CHARGE_STATES.values()),
72  device_class=SensorDeviceClass.ENUM,
73  value_fn=lambda value: CHARGE_STATES.get(cast(str, value)),
74  ),
76  key="charge_state_battery_level",
77  state_class=SensorStateClass.MEASUREMENT,
78  native_unit_of_measurement=PERCENTAGE,
79  device_class=SensorDeviceClass.BATTERY,
80  ),
82  key="charge_state_usable_battery_level",
83  state_class=SensorStateClass.MEASUREMENT,
84  native_unit_of_measurement=PERCENTAGE,
85  device_class=SensorDeviceClass.BATTERY,
86  entity_registry_enabled_default=False,
87  ),
89  key="charge_state_charge_energy_added",
90  state_class=SensorStateClass.TOTAL_INCREASING,
91  native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
92  device_class=SensorDeviceClass.ENERGY,
93  suggested_display_precision=1,
94  ),
96  key="charge_state_charger_power",
97  state_class=SensorStateClass.MEASUREMENT,
98  native_unit_of_measurement=UnitOfPower.KILO_WATT,
99  device_class=SensorDeviceClass.POWER,
100  ),
102  key="charge_state_charger_voltage",
103  state_class=SensorStateClass.MEASUREMENT,
104  native_unit_of_measurement=UnitOfElectricPotential.VOLT,
105  device_class=SensorDeviceClass.VOLTAGE,
106  entity_category=EntityCategory.DIAGNOSTIC,
107  ),
109  key="charge_state_charger_actual_current",
110  state_class=SensorStateClass.MEASUREMENT,
111  native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
112  device_class=SensorDeviceClass.CURRENT,
113  entity_category=EntityCategory.DIAGNOSTIC,
114  ),
116  key="charge_state_charge_rate",
117  state_class=SensorStateClass.MEASUREMENT,
118  native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
119  device_class=SensorDeviceClass.SPEED,
120  entity_category=EntityCategory.DIAGNOSTIC,
121  ),
123  key="charge_state_conn_charge_cable",
124  entity_category=EntityCategory.DIAGNOSTIC,
125  entity_registry_enabled_default=False,
126  ),
128  key="charge_state_fast_charger_type",
129  entity_category=EntityCategory.DIAGNOSTIC,
130  entity_registry_enabled_default=False,
131  ),
133  key="charge_state_battery_range",
134  state_class=SensorStateClass.MEASUREMENT,
135  native_unit_of_measurement=UnitOfLength.MILES,
136  device_class=SensorDeviceClass.DISTANCE,
137  suggested_display_precision=1,
138  ),
140  key="charge_state_est_battery_range",
141  state_class=SensorStateClass.MEASUREMENT,
142  native_unit_of_measurement=UnitOfLength.MILES,
143  device_class=SensorDeviceClass.DISTANCE,
144  suggested_display_precision=1,
145  entity_registry_enabled_default=False,
146  ),
148  key="charge_state_ideal_battery_range",
149  state_class=SensorStateClass.MEASUREMENT,
150  native_unit_of_measurement=UnitOfLength.MILES,
151  device_class=SensorDeviceClass.DISTANCE,
152  suggested_display_precision=1,
153  entity_registry_enabled_default=False,
154  ),
156  key="drive_state_speed",
157  state_class=SensorStateClass.MEASUREMENT,
158  native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
159  device_class=SensorDeviceClass.SPEED,
160  entity_registry_enabled_default=False,
161  value_fn=lambda value: value or 0,
162  ),
164  key="drive_state_power",
165  state_class=SensorStateClass.MEASUREMENT,
166  native_unit_of_measurement=UnitOfPower.KILO_WATT,
167  device_class=SensorDeviceClass.POWER,
168  entity_category=EntityCategory.DIAGNOSTIC,
169  entity_registry_enabled_default=False,
170  value_fn=lambda value: value or 0,
171  ),
173  key="drive_state_shift_state",
174  options=list(SHIFT_STATES.values()),
175  device_class=SensorDeviceClass.ENUM,
176  value_fn=lambda x: SHIFT_STATES.get(str(x), "p"),
177  entity_registry_enabled_default=False,
178  ),
180  key="vehicle_state_odometer",
181  state_class=SensorStateClass.TOTAL_INCREASING,
182  native_unit_of_measurement=UnitOfLength.MILES,
183  device_class=SensorDeviceClass.DISTANCE,
184  suggested_display_precision=0,
185  entity_category=EntityCategory.DIAGNOSTIC,
186  entity_registry_enabled_default=False,
187  ),
189  key="vehicle_state_tpms_pressure_fl",
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  entity_registry_enabled_default=False,
197  ),
199  key="vehicle_state_tpms_pressure_fr",
200  state_class=SensorStateClass.MEASUREMENT,
201  native_unit_of_measurement=UnitOfPressure.BAR,
202  suggested_unit_of_measurement=UnitOfPressure.PSI,
203  device_class=SensorDeviceClass.PRESSURE,
204  suggested_display_precision=1,
205  entity_category=EntityCategory.DIAGNOSTIC,
206  entity_registry_enabled_default=False,
207  ),
209  key="vehicle_state_tpms_pressure_rl",
210  state_class=SensorStateClass.MEASUREMENT,
211  native_unit_of_measurement=UnitOfPressure.BAR,
212  suggested_unit_of_measurement=UnitOfPressure.PSI,
213  device_class=SensorDeviceClass.PRESSURE,
214  suggested_display_precision=1,
215  entity_category=EntityCategory.DIAGNOSTIC,
216  entity_registry_enabled_default=False,
217  ),
219  key="vehicle_state_tpms_pressure_rr",
220  state_class=SensorStateClass.MEASUREMENT,
221  native_unit_of_measurement=UnitOfPressure.BAR,
222  suggested_unit_of_measurement=UnitOfPressure.PSI,
223  device_class=SensorDeviceClass.PRESSURE,
224  suggested_display_precision=1,
225  entity_category=EntityCategory.DIAGNOSTIC,
226  entity_registry_enabled_default=False,
227  ),
229  key="climate_state_inside_temp",
230  state_class=SensorStateClass.MEASUREMENT,
231  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
232  device_class=SensorDeviceClass.TEMPERATURE,
233  suggested_display_precision=1,
234  ),
236  key="climate_state_outside_temp",
237  state_class=SensorStateClass.MEASUREMENT,
238  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
239  device_class=SensorDeviceClass.TEMPERATURE,
240  suggested_display_precision=1,
241  ),
243  key="climate_state_driver_temp_setting",
244  state_class=SensorStateClass.MEASUREMENT,
245  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
246  device_class=SensorDeviceClass.TEMPERATURE,
247  suggested_display_precision=1,
248  entity_category=EntityCategory.DIAGNOSTIC,
249  entity_registry_enabled_default=False,
250  ),
252  key="climate_state_passenger_temp_setting",
253  state_class=SensorStateClass.MEASUREMENT,
254  native_unit_of_measurement=UnitOfTemperature.CELSIUS,
255  device_class=SensorDeviceClass.TEMPERATURE,
256  suggested_display_precision=1,
257  entity_category=EntityCategory.DIAGNOSTIC,
258  entity_registry_enabled_default=False,
259  ),
261  key="drive_state_active_route_traffic_minutes_delay",
262  state_class=SensorStateClass.MEASUREMENT,
263  native_unit_of_measurement=UnitOfTime.MINUTES,
264  device_class=SensorDeviceClass.DURATION,
265  entity_registry_enabled_default=False,
266  ),
268  key="drive_state_active_route_energy_at_arrival",
269  state_class=SensorStateClass.MEASUREMENT,
270  native_unit_of_measurement=PERCENTAGE,
271  device_class=SensorDeviceClass.BATTERY,
272  entity_category=EntityCategory.DIAGNOSTIC,
273  entity_registry_enabled_default=False,
274  ),
276  key="drive_state_active_route_miles_to_arrival",
277  state_class=SensorStateClass.MEASUREMENT,
278  native_unit_of_measurement=UnitOfLength.MILES,
279  device_class=SensorDeviceClass.DISTANCE,
280  ),
281 )
282 
283 
284 @dataclass(frozen=True, kw_only=True)
286  """Describes Teslemetry Sensor entity."""
287 
288  variance: int
289 
290 
291 VEHICLE_TIME_DESCRIPTIONS: tuple[TeslemetryTimeEntityDescription, ...] = (
293  key="charge_state_minutes_to_full_charge",
294  device_class=SensorDeviceClass.TIMESTAMP,
295  entity_category=EntityCategory.DIAGNOSTIC,
296  variance=4,
297  ),
299  key="drive_state_active_route_minutes_to_arrival",
300  device_class=SensorDeviceClass.TIMESTAMP,
301  variance=1,
302  ),
303 )
304 
305 ENERGY_LIVE_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
307  key="solar_power",
308  state_class=SensorStateClass.MEASUREMENT,
309  native_unit_of_measurement=UnitOfPower.WATT,
310  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
311  suggested_display_precision=2,
312  device_class=SensorDeviceClass.POWER,
313  ),
315  key="energy_left",
316  state_class=SensorStateClass.MEASUREMENT,
317  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
318  suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
319  suggested_display_precision=2,
320  device_class=SensorDeviceClass.ENERGY_STORAGE,
321  entity_category=EntityCategory.DIAGNOSTIC,
322  ),
324  key="total_pack_energy",
325  state_class=SensorStateClass.MEASUREMENT,
326  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
327  suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
328  suggested_display_precision=2,
329  device_class=SensorDeviceClass.ENERGY_STORAGE,
330  entity_category=EntityCategory.DIAGNOSTIC,
331  entity_registry_enabled_default=False,
332  ),
334  key="percentage_charged",
335  state_class=SensorStateClass.MEASUREMENT,
336  native_unit_of_measurement=PERCENTAGE,
337  device_class=SensorDeviceClass.BATTERY,
338  suggested_display_precision=2,
339  ),
341  key="battery_power",
342  state_class=SensorStateClass.MEASUREMENT,
343  native_unit_of_measurement=UnitOfPower.WATT,
344  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
345  suggested_display_precision=2,
346  device_class=SensorDeviceClass.POWER,
347  ),
349  key="load_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="grid_power",
358  state_class=SensorStateClass.MEASUREMENT,
359  native_unit_of_measurement=UnitOfPower.WATT,
360  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
361  suggested_display_precision=2,
362  device_class=SensorDeviceClass.POWER,
363  ),
365  key="grid_services_power",
366  state_class=SensorStateClass.MEASUREMENT,
367  native_unit_of_measurement=UnitOfPower.WATT,
368  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
369  suggested_display_precision=2,
370  device_class=SensorDeviceClass.POWER,
371  ),
373  key="generator_power",
374  state_class=SensorStateClass.MEASUREMENT,
375  native_unit_of_measurement=UnitOfPower.WATT,
376  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
377  suggested_display_precision=2,
378  device_class=SensorDeviceClass.POWER,
379  entity_registry_enabled_default=False,
380  ),
382  key="island_status",
383  device_class=SensorDeviceClass.ENUM,
384  options=[
385  "on_grid",
386  "off_grid",
387  "off_grid_intentional",
388  "off_grid_unintentional",
389  "island_status_unknown",
390  ],
391  ),
392 )
393 
394 WALL_CONNECTOR_DESCRIPTIONS: tuple[TeslemetrySensorEntityDescription, ...] = (
396  key="wall_connector_state",
397  entity_category=EntityCategory.DIAGNOSTIC,
398  entity_registry_enabled_default=False,
399  ),
401  key="wall_connector_fault_state",
402  entity_category=EntityCategory.DIAGNOSTIC,
403  entity_registry_enabled_default=False,
404  ),
406  key="wall_connector_power",
407  state_class=SensorStateClass.MEASUREMENT,
408  native_unit_of_measurement=UnitOfPower.WATT,
409  suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
410  suggested_display_precision=2,
411  device_class=SensorDeviceClass.POWER,
412  ),
414  key="vin",
415  value_fn=lambda vin: vin or "disconnected",
416  ),
417 )
418 
419 ENERGY_INFO_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
421  key="vpp_backup_reserve_percent",
422  entity_category=EntityCategory.DIAGNOSTIC,
423  device_class=SensorDeviceClass.BATTERY,
424  native_unit_of_measurement=PERCENTAGE,
425  ),
426  SensorEntityDescription(key="version"),
427 )
428 
429 ENERGY_HISTORY_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = tuple(
431  key=key,
432  device_class=SensorDeviceClass.ENERGY,
433  native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
434  suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
435  suggested_display_precision=2,
436  state_class=SensorStateClass.TOTAL_INCREASING,
437  entity_registry_enabled_default=(
438  key.startswith("total") or key == "grid_energy_imported"
439  ),
440  )
441  for key in ENERGY_HISTORY_FIELDS
442 )
443 
444 
446  hass: HomeAssistant,
447  entry: TeslemetryConfigEntry,
448  async_add_entities: AddEntitiesCallback,
449 ) -> None:
450  """Set up the Teslemetry sensor platform from a config entry."""
452  chain(
453  ( # Add vehicles
454  TeslemetryVehicleSensorEntity(vehicle, description)
455  for vehicle in entry.runtime_data.vehicles
456  for description in VEHICLE_DESCRIPTIONS
457  ),
458  ( # Add vehicles time sensors
459  TeslemetryVehicleTimeSensorEntity(vehicle, description)
460  for vehicle in entry.runtime_data.vehicles
461  for description in VEHICLE_TIME_DESCRIPTIONS
462  ),
463  ( # Add energy site live
464  TeslemetryEnergyLiveSensorEntity(energysite, description)
465  for energysite in entry.runtime_data.energysites
466  for description in ENERGY_LIVE_DESCRIPTIONS
467  if description.key in energysite.live_coordinator.data
468  ),
469  ( # Add wall connectors
470  TeslemetryWallConnectorSensorEntity(energysite, din, description)
471  for energysite in entry.runtime_data.energysites
472  for din in energysite.live_coordinator.data.get("wall_connectors", {})
473  for description in WALL_CONNECTOR_DESCRIPTIONS
474  ),
475  ( # Add energy site info
476  TeslemetryEnergyInfoSensorEntity(energysite, description)
477  for energysite in entry.runtime_data.energysites
478  for description in ENERGY_INFO_DESCRIPTIONS
479  if description.key in energysite.info_coordinator.data
480  ),
481  ( # Add energy history sensor
482  TeslemetryEnergyHistorySensorEntity(energysite, description)
483  for energysite in entry.runtime_data.energysites
484  for description in ENERGY_HISTORY_DESCRIPTIONS
485  if energysite.history_coordinator
486  ),
487  )
488  )
489 
490 
492  """Base class for Teslemetry vehicle metric sensors."""
493 
494  entity_description: TeslemetrySensorEntityDescription
495 
496  def __init__(
497  self,
498  data: TeslemetryVehicleData,
499  description: TeslemetrySensorEntityDescription,
500  ) -> None:
501  """Initialize the sensor."""
502  self.entity_descriptionentity_description = description
503  super().__init__(data, description.key)
504 
505  def _async_update_attrs(self) -> None:
506  """Update the attributes of the sensor."""
507  if self.hashas:
508  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self._value_value_value)
509  else:
510  self._attr_native_value_attr_native_value = None
511 
512 
514  """Base class for Teslemetry vehicle time sensors."""
515 
516  entity_description: TeslemetryTimeEntityDescription
517 
518  def __init__(
519  self,
520  data: TeslemetryVehicleData,
521  description: TeslemetryTimeEntityDescription,
522  ) -> None:
523  """Initialize the sensor."""
524  self.entity_descriptionentity_description = description
525  self._get_timestamp_get_timestamp = ignore_variance(
526  func=lambda value: dt_util.now() + timedelta(minutes=value),
527  ignored_variance=timedelta(minutes=description.variance),
528  )
529 
530  super().__init__(data, description.key)
531 
532  def _async_update_attrs(self) -> None:
533  """Update the attributes of the sensor."""
534  self._attr_available_attr_available = isinstance(self._value_value_value, int | float) and self._value_value_value > 0
535  if self._attr_available_attr_available:
536  self._attr_native_value_attr_native_value = self._get_timestamp_get_timestamp(self._value_value_value)
537 
538 
540  """Base class for Teslemetry energy site metric sensors."""
541 
542  entity_description: SensorEntityDescription
543 
544  def __init__(
545  self,
546  data: TeslemetryEnergyData,
547  description: SensorEntityDescription,
548  ) -> None:
549  """Initialize the sensor."""
550  self.entity_descriptionentity_description = description
551  super().__init__(data, description.key)
552 
553  def _async_update_attrs(self) -> None:
554  """Update the attributes of the sensor."""
555  self._attr_available_attr_available = not self.is_noneis_none
556  self._attr_native_value_attr_native_value = self._value_value
557 
558 
560  """Base class for Teslemetry energy site metric sensors."""
561 
562  entity_description: TeslemetrySensorEntityDescription
563 
564  def __init__(
565  self,
566  data: TeslemetryEnergyData,
567  din: str,
568  description: TeslemetrySensorEntityDescription,
569  ) -> None:
570  """Initialize the sensor."""
571  self.entity_descriptionentity_description = description
572  super().__init__(
573  data,
574  din,
575  description.key,
576  )
577 
578  def _async_update_attrs(self) -> None:
579  """Update the attributes of the sensor."""
580  if self.existsexists:
581  self._attr_native_value_attr_native_value = self.entity_descriptionentity_description.value_fn(self._value_value_value)
582 
583 
585  """Base class for Teslemetry energy site metric sensors."""
586 
587  entity_description: SensorEntityDescription
588 
589  def __init__(
590  self,
591  data: TeslemetryEnergyData,
592  description: SensorEntityDescription,
593  ) -> None:
594  """Initialize the sensor."""
595  self.entity_descriptionentity_description = description
596  super().__init__(data, description.key)
597 
598  def _async_update_attrs(self) -> None:
599  """Update the attributes of the sensor."""
600  self._attr_available_attr_available = not self.is_noneis_none
601  self._attr_native_value_attr_native_value = self._value_value
602 
603 
605  """Base class for Tesla Fleet energy site metric sensors."""
606 
607  entity_description: SensorEntityDescription
608 
609  def __init__(
610  self,
611  data: TeslemetryEnergyData,
612  description: SensorEntityDescription,
613  ) -> None:
614  """Initialize the sensor."""
615  self.entity_descriptionentity_description = description
616  super().__init__(data, description.key)
617 
618  def _async_update_attrs(self) -> None:
619  """Update the attributes of the sensor."""
620  self._attr_native_value_attr_native_value = self._value_value
None __init__(self, TeslemetryEnergyData data, SensorEntityDescription description)
Definition: sensor.py:613
None __init__(self, TeslemetryEnergyData data, SensorEntityDescription description)
Definition: sensor.py:593
None __init__(self, TeslemetryEnergyData data, SensorEntityDescription description)
Definition: sensor.py:548
None __init__(self, TeslemetryVehicleData data, TeslemetrySensorEntityDescription description)
Definition: sensor.py:500
None __init__(self, TeslemetryVehicleData data, TeslemetryTimeEntityDescription description)
Definition: sensor.py:522
None __init__(self, TeslemetryEnergyData data, str din, TeslemetrySensorEntityDescription description)
Definition: sensor.py:569
None async_setup_entry(HomeAssistant hass, TeslemetryConfigEntry entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:449