Files

2007 lines
75 KiB
Python

"""Sensor platform for Teslemetry integration."""
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Any, override
from tesla_fleet_api import firmware_at_least
from teslemetry_stream import TeslemetryStream, TeslemetryStreamVehicle
from homeassistant.components.sensor import (
RestoreSensor,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
DEGREE,
PERCENTAGE,
EntityCategory,
UnitOfElectricCurrent,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfLength,
UnitOfPower,
UnitOfPressure,
UnitOfSpeed,
UnitOfTemperature,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from homeassistant.util import dt as dt_util
from homeassistant.util.variance import ignore_variance
from . import TeslemetryConfigEntry
from .const import ENERGY_HISTORY_FIELDS
from .entity import (
TeslemetryEnergyHistoryEntity,
TeslemetryEnergyInfoEntity,
TeslemetryEnergyLiveEntity,
TeslemetryVehiclePollingEntity,
TeslemetryVehicleStreamEntity,
TeslemetryWallConnectorEntity,
)
from .models import TeslemetryEnergyData, TeslemetryVehicleData
PARALLEL_UPDATES = 0
# Teslemetry streams TPMS pressure in atmospheres; entities are declared in bar.
ATM_TO_BAR = 1.01325
# Tesla only reports the self-driving/mileage-since-reset fields (258-259) on HW4
# vehicles, identified by this driver-assist capability in the vehicle config.
DRIVER_ASSIST_HW4 = "TeslaAP4"
BMS_STATES = {
"Standby": "standby",
"Drive": "drive",
"Support": "support",
"Charge": "charge",
"FEIM": "full_electric_in_motion",
"ClearFault": "clear_fault",
"Fault": "fault",
"Weld": "weld",
"Test": "test",
"SNA": "system_not_available",
}
CHARGE_STATES = {
"Starting": "starting",
"Charging": "charging",
"Stopped": "stopped",
"Complete": "complete",
"Disconnected": "disconnected",
"NoPower": "no_power",
}
DRIVE_INVERTER_STATES = {
"Unavailable": "not_available",
"Standby": "standby",
"Fault": "fault",
"Abort": "abort",
"Enable": "enabled",
}
SHIFT_STATES = {"P": "p", "D": "d", "R": "r", "N": "n"}
SENTRY_MODE_STATES = {
"Off": "off",
"Idle": "idle",
"Armed": "armed",
"Aware": "aware",
"Panic": "panic",
"Quiet": "quiet",
}
POWER_SHARE_STATES = {
"Inactive": "inactive",
"Handshaking": "handshaking",
"Init": "init",
"Enabled": "enabled",
"EnabledReconnectingSoon": "reconnecting",
"Stopped": "stopped",
}
POWER_SHARE_STOP_REASONS = {
"None": "none",
"SOCTooLow": "soc_too_low",
"Retry": "retry",
"Fault": "fault",
"User": "user",
"Reconnecting": "reconnecting",
"Authentication": "authentication",
}
POWER_SHARE_TYPES = {
"None": "none",
"Load": "load",
"Home": "home",
}
CHARGE_CABLE_TYPES = {
"IEC": "iec",
"SAE": "sae",
"GB_AC": "gb_ac",
"GB_DC": "gb_dc",
}
FORWARD_COLLISION_SENSITIVITIES = {
"Off": "off",
"Late": "late",
"Average": "average",
"Early": "early",
}
GUEST_MODE_MOBILE_ACCESS_STATES = {
"Init": "init",
"NotAuthenticated": "not_authenticated",
"Authenticated": "authenticated",
"AbortedDriving": "aborted_driving",
"AbortedUsingRemoteStart": "aborted_using_remote_start",
"AbortedUsingBLEKeys": "aborted_using_ble_keys",
"AbortedValetMode": "aborted_valet_mode",
"AbortedGuestModeOff": "aborted_guest_mode_off",
"AbortedDriveAuthTimeExceeded": "aborted_drive_auth_time_exceeded",
"AbortedNoDataReceived": "aborted_no_data_received",
"RequestingFromMothership": "requesting_from_mothership",
"RequestingFromAuthD": "requesting_from_auth_d",
"AbortedFetchFailed": "aborted_fetch_failed",
"AbortedBadDataReceived": "aborted_bad_data_received",
"ShowingQRCode": "showing_qr_code",
"SwipedAway": "swiped_away",
"DismissedQRCodeExpired": "dismissed_qr_code_expired",
"SucceededPairedNewBLEKey": "succeeded_paired_new_ble_key",
}
HVAC_POWER_STATES = {
"Off": "off",
"On": "on",
"Precondition": "precondition",
"OverheatProtect": "overheat_protection",
}
LANE_ASSIST_LEVELS = {
"None": "off",
"Warning": "warning",
"Assist": "assist",
}
SCHEDULED_CHARGING_MODES = {
"Off": "off",
"StartAt": "start_at",
"DepartBy": "depart_by",
}
SPEED_ASSIST_LEVELS = {
"None": "none",
"Display": "display",
"Chime": "chime",
}
TONNEAU_TENT_MODE_STATES = {
"Inactive": "inactive",
"Moving": "moving",
"Failed": "failed",
"Active": "active",
}
TURN_SIGNAL_STATES = {
"Off": "off",
"Left": "left",
"Right": "right",
"Both": "both",
}
@dataclass(frozen=True, kw_only=True)
class TeslemetryVehicleSensorEntityDescription(SensorEntityDescription):
"""Describes Teslemetry Sensor entity."""
polling: bool = False
polling_value_fn: Callable[[StateType], StateType] = lambda x: x
nullable: bool = False
streaming_listener: (
Callable[
[TeslemetryStreamVehicle, Callable[[StateType], None]],
Callable[[], None],
]
| None
) = None
streaming_firmware: str = "2024.26"
requires_hw4: bool = False
VEHICLE_DESCRIPTIONS: tuple[TeslemetryVehicleSensorEntityDescription, ...] = (
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charging_state",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_DetailedChargeState(
lambda value: callback(None if value is None else CHARGE_STATES.get(value))
),
polling_value_fn=lambda value: CHARGE_STATES.get(str(value)),
options=list(CHARGE_STATES.values()),
device_class=SensorDeviceClass.ENUM,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_battery_level",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_BatteryLevel(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_usable_battery_level",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_Soc(callback),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
entity_registry_enabled_default=False,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charge_energy_added",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_ACChargingEnergyIn(
callback
),
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charger_power",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_ACChargingPower(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charger_voltage",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_ChargerVoltage(
callback
),
streaming_firmware="2024.44.32",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charger_actual_current",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_ChargeAmps(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_charge_rate",
polling=True,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.SPEED,
entity_category=EntityCategory.DIAGNOSTIC,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_conn_charge_cable",
polling=True,
polling_value_fn=lambda value: CHARGE_CABLE_TYPES.get(str(value)),
streaming_listener=lambda vehicle, callback: vehicle.listen_ChargingCableType(
lambda value: callback(
None if value is None else CHARGE_CABLE_TYPES.get(value)
)
),
options=list(CHARGE_CABLE_TYPES.values()),
device_class=SensorDeviceClass.ENUM,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_fast_charger_type",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_FastChargerType(
callback
),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_battery_range",
polling=True,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
suggested_display_precision=1,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_est_battery_range",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_EstBatteryRange(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_state_ideal_battery_range",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_IdealBatteryRange(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_speed",
polling=True,
polling_value_fn=lambda value: value or 0,
streaming_listener=lambda vehicle, callback: vehicle.listen_VehicleSpeed(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.SPEED,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_power",
polling=True,
polling_value_fn=lambda value: value or 0,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_shift_state",
polling=True,
polling_value_fn=lambda x: SHIFT_STATES.get(str(x), "p"),
nullable=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_Gear(
lambda value: callback("p" if value is None else value.lower())
),
options=list(SHIFT_STATES.values()),
device_class=SensorDeviceClass.ENUM,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="vehicle_state_odometer",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_Odometer(callback),
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
suggested_display_precision=0,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="vehicle_state_tpms_pressure_fl",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsPressureFl(
lambda x: callback(None) if x is None else callback(x * ATM_TO_BAR)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.BAR,
suggested_unit_of_measurement=UnitOfPressure.PSI,
device_class=SensorDeviceClass.PRESSURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="vehicle_state_tpms_pressure_fr",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsPressureFr(
lambda x: callback(None) if x is None else callback(x * ATM_TO_BAR)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.BAR,
suggested_unit_of_measurement=UnitOfPressure.PSI,
device_class=SensorDeviceClass.PRESSURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="vehicle_state_tpms_pressure_rl",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsPressureRl(
lambda x: callback(None) if x is None else callback(x * ATM_TO_BAR)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.BAR,
suggested_unit_of_measurement=UnitOfPressure.PSI,
device_class=SensorDeviceClass.PRESSURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="vehicle_state_tpms_pressure_rr",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsPressureRr(
lambda x: callback(None) if x is None else callback(x * ATM_TO_BAR)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPressure.BAR,
suggested_unit_of_measurement=UnitOfPressure.PSI,
device_class=SensorDeviceClass.PRESSURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="climate_state_inside_temp",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_InsideTemp(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="climate_state_outside_temp",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_OutsideTemp(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
),
TeslemetryVehicleSensorEntityDescription(
key="climate_state_driver_temp_setting",
polling=True,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="climate_state_passenger_temp_setting",
polling=True,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="hvac_left_temperature_request",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_HvacLeftTemperatureRequest(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="hvac_right_temperature_request",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_HvacRightTemperatureRequest(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
suggested_display_precision=1,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_active_route_destination",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_DestinationName(
callback
),
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_active_route_traffic_minutes_delay",
polling=True,
streaming_listener=lambda vehicle, callback: (
vehicle.listen_RouteTrafficMinutesDelay(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=SensorDeviceClass.DURATION,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_active_route_energy_at_arrival",
polling=True,
streaming_listener=lambda vehicle, callback: (
vehicle.listen_ExpectedEnergyPercentAtTripArrival(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="drive_state_active_route_miles_to_arrival",
polling=True,
streaming_listener=lambda vehicle, callback: vehicle.listen_MilesToArrival(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
),
TeslemetryVehicleSensorEntityDescription(
key="bms_state",
streaming_listener=lambda vehicle, callback: vehicle.listen_BMSState(
lambda value: callback(None if value is None else BMS_STATES.get(value))
),
device_class=SensorDeviceClass.ENUM,
options=list(BMS_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="brake_pedal_position",
streaming_listener=lambda vehicle, callback: vehicle.listen_BrakePedalPos(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="brick_voltage_max",
streaming_listener=lambda vehicle, callback: vehicle.listen_BrickVoltageMax(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="brick_voltage_min",
streaming_listener=lambda vehicle, callback: vehicle.listen_BrickVoltageMin(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="cruise_follow_distance",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_CruiseFollowDistance(callback)
),
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.SECONDS,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="cruise_set_speed",
streaming_listener=lambda vehicle, callback: vehicle.listen_CruiseSetSpeed(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.SPEED,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="current_limit_mph",
streaming_listener=lambda vehicle, callback: vehicle.listen_CurrentLimitMph(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.SPEED,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="dc_charging_energy_in",
streaming_listener=lambda vehicle, callback: vehicle.listen_DCChargingEnergyIn(
callback
),
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="dc_charging_power",
streaming_listener=lambda vehicle, callback: vehicle.listen_DCChargingPower(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_axle_speed_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiAxleSpeedF(
callback
),
native_unit_of_measurement="rad/s",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_axle_speed_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiAxleSpeedR(
callback
),
native_unit_of_measurement="rad/s",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_axle_speed_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiAxleSpeedREL(
callback
),
native_unit_of_measurement="rad/s",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_axle_speed_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiAxleSpeedRER(
callback
),
native_unit_of_measurement="rad/s",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_heatsink_tf",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiHeatsinkTF(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_heatsink_tr",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiHeatsinkTR(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_heatsink_trel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiHeatsinkTREL(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_heatsink_trer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiHeatsinkTRER(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_inverter_tf",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiInverterTF(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_inverter_tr",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiInverterTR(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_inverter_trel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiInverterTREL(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_inverter_trer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiInverterTRER(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_motor_current_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiMotorCurrentF(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_motor_current_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiMotorCurrentR(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_motor_current_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiMotorCurrentREL(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_motor_current_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiMotorCurrentRER(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_slave_torque_cmd",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiSlaveTorqueCmd(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_state_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStateF(
lambda value: callback(
None if value is None else DRIVE_INVERTER_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(DRIVE_INVERTER_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_state_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStateR(
lambda value: callback(
None if value is None else DRIVE_INVERTER_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(DRIVE_INVERTER_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_state_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStateREL(
lambda value: callback(
None if value is None else DRIVE_INVERTER_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(DRIVE_INVERTER_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_state_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStateRER(
lambda value: callback(
None if value is None else DRIVE_INVERTER_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(DRIVE_INVERTER_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_stator_temp_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStatorTempF(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_stator_temp_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStatorTempR(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_stator_temp_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStatorTempREL(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_stator_temp_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiStatorTempRER(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_torque_actual_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiTorqueActualF(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_torque_actual_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiTorqueActualR(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_torque_actual_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiTorqueActualREL(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_torque_actual_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiTorqueActualRER(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_torquemotor",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiTorquemotor(
callback
),
native_unit_of_measurement="Nm", # Newton-meters
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_vbat_f",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiVBatF(callback),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_vbat_r",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiVBatR(callback),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_vbat_rel",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiVBatREL(callback),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="di_vbat_rer",
streaming_listener=lambda vehicle, callback: vehicle.listen_DiVBatRER(callback),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="sentry_mode",
streaming_listener=lambda vehicle, callback: vehicle.listen_SentryMode(
lambda value: callback(
None if value is None else SENTRY_MODE_STATES.get(value)
)
),
options=list(SENTRY_MODE_STATES.values()),
device_class=SensorDeviceClass.ENUM,
),
TeslemetryVehicleSensorEntityDescription(
key="energy_remaining",
streaming_listener=lambda vehicle, callback: vehicle.listen_EnergyRemaining(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY_STORAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="estimated_hours_to_charge_termination",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_EstimatedHoursToChargeTermination(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.HOURS,
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="forward_collision_warning",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_ForwardCollisionWarning(
lambda value: callback(
None
if value is None
else FORWARD_COLLISION_SENSITIVITIES.get(value)
)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(FORWARD_COLLISION_SENSITIVITIES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="gps_heading",
streaming_listener=lambda vehicle, callback: vehicle.listen_GpsHeading(
callback
),
native_unit_of_measurement=DEGREE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="guest_mode_mobile_access_state",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_GuestModeMobileAccessState(
lambda value: callback(
None
if value is None
else GUEST_MODE_MOBILE_ACCESS_STATES.get(value)
)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(GUEST_MODE_MOBILE_ACCESS_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="homelink_device_count",
streaming_listener=lambda vehicle, callback: vehicle.listen_HomelinkDeviceCount(
callback
),
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="hvac_fan_speed",
streaming_listener=lambda vehicle, callback: vehicle.listen_HvacFanSpeed(
lambda x: callback(None) if x is None else callback(x * 10)
),
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="hvac_fan_status",
streaming_listener=lambda vehicle, callback: vehicle.listen_HvacFanStatus(
lambda x: callback(None) if x is None else callback(x * 10)
),
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="isolation_resistance",
# Teslemetry streams this field in kΩ; declare the unit as kΩ.
streaming_listener=lambda vehicle, callback: vehicle.listen_IsolationResistance(
callback
),
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement="kΩ",
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="lane_departure_avoidance",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_LaneDepartureAvoidance(
lambda value: callback(
None if value is None else LANE_ASSIST_LEVELS.get(value)
)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(LANE_ASSIST_LEVELS.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="lateral_acceleration",
streaming_listener=lambda vehicle, callback: vehicle.listen_LateralAcceleration(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement="g",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="lifetime_energy_gained_regen",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_LifetimeEnergyGainedRegen(callback)
),
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="lifetime_energy_used",
streaming_listener=lambda vehicle, callback: vehicle.listen_LifetimeEnergyUsed(
callback
),
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="longitudinal_acceleration",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_LongitudinalAcceleration(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement="g",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="miles_since_reset",
streaming_listener=lambda vehicle, callback: vehicle.listen_MilesSinceReset(
callback
),
streaming_firmware="2025.44.25.5",
requires_hw4=True,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="module_temp_max",
streaming_listener=lambda vehicle, callback: vehicle.listen_ModuleTempMax(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="module_temp_min",
streaming_listener=lambda vehicle, callback: vehicle.listen_ModuleTempMin(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="pack_current",
streaming_listener=lambda vehicle, callback: vehicle.listen_PackCurrent(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="pack_voltage",
streaming_listener=lambda vehicle, callback: vehicle.listen_PackVoltage(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfElectricPotential.VOLT,
device_class=SensorDeviceClass.VOLTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="paired_phone_key_and_key_fob_qty",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_PairedPhoneKeyAndKeyFobQty(callback)
),
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="pedal_position",
streaming_listener=lambda vehicle, callback: vehicle.listen_PedalPosition(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="powershare_hours_left",
streaming_listener=lambda vehicle, callback: vehicle.listen_PowershareHoursLeft(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.HOURS,
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="powershare_instantaneous_power_kw",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_PowershareInstantaneousPowerKW(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.KILO_WATT,
device_class=SensorDeviceClass.POWER,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="powershare_status",
streaming_listener=lambda vehicle, callback: vehicle.listen_PowershareStatus(
lambda value: callback(
None if value is None else POWER_SHARE_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(POWER_SHARE_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="powershare_stop_reason",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_PowershareStopReason(
lambda value: callback(
None if value is None else POWER_SHARE_STOP_REASONS.get(value)
)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(POWER_SHARE_STOP_REASONS.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="powershare_type",
streaming_listener=lambda vehicle, callback: vehicle.listen_PowershareType(
lambda value: callback(
None if value is None else POWER_SHARE_TYPES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(POWER_SHARE_TYPES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="rated_range",
streaming_listener=lambda vehicle, callback: vehicle.listen_RatedRange(
callback
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="scheduled_charging_mode",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_ScheduledChargingMode(
lambda value: callback(
None if value is None else SCHEDULED_CHARGING_MODES.get(value)
)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(SCHEDULED_CHARGING_MODES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="self_driving_miles_since_reset",
streaming_listener=(
lambda vehicle, callback: vehicle.listen_SelfDrivingMilesSinceReset(
callback
)
),
streaming_firmware="2025.44.25.5",
requires_hw4=True,
state_class=SensorStateClass.TOTAL_INCREASING,
native_unit_of_measurement=UnitOfLength.MILES,
device_class=SensorDeviceClass.DISTANCE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="software_update_expected_duration_minutes",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_SoftwareUpdateExpectedDurationMinutes(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTime.MINUTES,
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="speed_limit_warning",
streaming_listener=lambda vehicle, callback: vehicle.listen_SpeedLimitWarning(
lambda value: callback(
None if value is None else SPEED_ASSIST_LEVELS.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(SPEED_ASSIST_LEVELS.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="tonneau_tent_mode",
streaming_listener=lambda vehicle, callback: vehicle.listen_TonneauTentMode(
lambda value: callback(
None if value is None else TONNEAU_TENT_MODE_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(TONNEAU_TENT_MODE_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="tpms_hard_warnings",
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsHardWarnings(
callback
),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="tpms_soft_warnings",
streaming_listener=lambda vehicle, callback: vehicle.listen_TpmsSoftWarnings(
callback
),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="lights_turn_signal",
streaming_listener=lambda vehicle, callback: vehicle.listen_LightsTurnSignal(
lambda value: callback(
None if value is None else TURN_SIGNAL_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(TURN_SIGNAL_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="charge_rate_mile_per_hour",
streaming_listener=lambda vehicle, callback: (
vehicle.listen_ChargeRateMilePerHour(callback)
),
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfSpeed.MILES_PER_HOUR,
device_class=SensorDeviceClass.SPEED,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryVehicleSensorEntityDescription(
key="hvac_power_state",
streaming_listener=lambda vehicle, callback: vehicle.listen_HvacPower(
lambda value: callback(
None if value is None else HVAC_POWER_STATES.get(value)
)
),
device_class=SensorDeviceClass.ENUM,
options=list(HVAC_POWER_STATES.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
)
@dataclass(frozen=True, kw_only=True)
class TeslemetryTimeEntityDescription(SensorEntityDescription):
"""Describes Teslemetry Sensor entity."""
variance: int
streaming_listener: Callable[
[TeslemetryStreamVehicle, Callable[[float | None], None]],
Callable[[], None],
]
streaming_firmware: str = "2024.26"
streaming_unit: str
VEHICLE_TIME_DESCRIPTIONS: tuple[TeslemetryTimeEntityDescription, ...] = (
TeslemetryTimeEntityDescription(
key="charge_state_minutes_to_full_charge",
streaming_listener=lambda vehicle, callback: vehicle.listen_TimeToFullCharge(
callback
),
streaming_unit="hours",
device_class=SensorDeviceClass.TIMESTAMP,
entity_category=EntityCategory.DIAGNOSTIC,
variance=4,
),
TeslemetryTimeEntityDescription(
key="drive_state_active_route_minutes_to_arrival",
streaming_listener=lambda vehicle, callback: vehicle.listen_MinutesToArrival(
callback
),
streaming_unit="minutes",
device_class=SensorDeviceClass.TIMESTAMP,
variance=1,
),
)
@dataclass(frozen=True, kw_only=True)
class TeslemetryEnergySensorEntityDescription(SensorEntityDescription):
"""Describes Teslemetry Sensor entity."""
value_fn: Callable[[StateType], StateType | datetime] = lambda x: x
ENERGY_LIVE_DESCRIPTIONS: tuple[TeslemetryEnergySensorEntityDescription, ...] = (
TeslemetryEnergySensorEntityDescription(
key="solar_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetryEnergySensorEntityDescription(
key="energy_left",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY_STORAGE,
entity_category=EntityCategory.DIAGNOSTIC,
),
TeslemetryEnergySensorEntityDescription(
key="total_pack_energy",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
device_class=SensorDeviceClass.ENERGY_STORAGE,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetryEnergySensorEntityDescription(
key="percentage_charged",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
suggested_display_precision=2,
value_fn=lambda value: value or 0,
),
TeslemetryEnergySensorEntityDescription(
key="battery_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetryEnergySensorEntityDescription(
key="load_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetryEnergySensorEntityDescription(
key="grid_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetryEnergySensorEntityDescription(
key="grid_services_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetryEnergySensorEntityDescription(
key="generator_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
entity_registry_enabled_default=False,
),
TeslemetryEnergySensorEntityDescription(
key="island_status",
device_class=SensorDeviceClass.ENUM,
options=[
"on_grid",
"off_grid",
"off_grid_intentional",
"off_grid_unintentional",
"island_status_unknown",
],
),
)
@dataclass(frozen=True, kw_only=True)
class TeslemetrySensorEntityDescription(SensorEntityDescription):
"""Describes Teslemetry Sensor entity."""
value_fn: Callable[[StateType], StateType] = lambda x: x
WALL_CONNECTOR_DESCRIPTIONS: tuple[TeslemetrySensorEntityDescription, ...] = (
TeslemetrySensorEntityDescription(
key="wall_connector_state",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetrySensorEntityDescription(
key="wall_connector_fault_state",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
TeslemetrySensorEntityDescription(
key="wall_connector_power",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfPower.WATT,
suggested_unit_of_measurement=UnitOfPower.KILO_WATT,
suggested_display_precision=2,
device_class=SensorDeviceClass.POWER,
),
TeslemetrySensorEntityDescription(
key="vin",
value_fn=lambda vin: vin or "disconnected",
),
)
ENERGY_INFO_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(
key="vpp_backup_reserve_percent",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
),
)
ENERGY_HISTORY_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = tuple(
SensorEntityDescription(
key=key,
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.WATT_HOUR,
suggested_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
suggested_display_precision=2,
state_class=SensorStateClass.TOTAL_INCREASING,
entity_registry_enabled_default=(
key.startswith("total") or key == "grid_energy_imported"
),
)
for key in ENERGY_HISTORY_FIELDS
)
async def async_setup_entry(
hass: HomeAssistant,
entry: TeslemetryConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Teslemetry sensor platform from a config entry."""
entities: list[SensorEntity] = []
for vehicle in entry.runtime_data.vehicles:
for description in VEHICLE_DESCRIPTIONS:
if (
not vehicle.poll
and description.streaming_listener
and firmware_at_least(vehicle.firmware, description.streaming_firmware)
and (
not description.requires_hw4
or vehicle.coordinator.data.get("vehicle_config_driver_assist")
== DRIVER_ASSIST_HW4
)
):
entities.append(TeslemetryStreamSensorEntity(vehicle, description))
elif description.polling:
entities.append(TeslemetryVehicleSensorEntity(vehicle, description))
for time_description in VEHICLE_TIME_DESCRIPTIONS:
if not vehicle.poll and firmware_at_least(
vehicle.firmware, time_description.streaming_firmware
):
entities.append(
TeslemetryStreamTimeSensorEntity(vehicle, time_description)
)
else:
entities.append(
TeslemetryVehicleTimeSensorEntity(vehicle, time_description)
)
entities.extend(
TeslemetryEnergyLiveSensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites
if energysite.live_coordinator
for description in ENERGY_LIVE_DESCRIPTIONS
if description.key in energysite.live_coordinator.data
or description.key == "percentage_charged"
)
entities.extend(
TeslemetryWallConnectorSensorEntity(energysite, din, description)
for energysite in entry.runtime_data.energysites
if energysite.live_coordinator
for din in energysite.live_coordinator.data.get("wall_connectors", {})
for description in WALL_CONNECTOR_DESCRIPTIONS
)
entities.extend(
TeslemetryEnergyInfoSensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites
for description in ENERGY_INFO_DESCRIPTIONS
if description.key in energysite.info_coordinator.data
)
entities.extend(
TeslemetryEnergyHistorySensorEntity(energysite, description)
for energysite in entry.runtime_data.energysites
for description in ENERGY_HISTORY_DESCRIPTIONS
if energysite.history_coordinator is not None
)
if entry.runtime_data.stream is not None:
entities.extend(
(
TeslemetryCreditBalanceSensor(
entry.unique_id or entry.entry_id, entry.runtime_data.stream
),
TeslemetryCreditQuotaSensor(
entry.unique_id or entry.entry_id, entry.runtime_data.stream
),
)
)
async_add_entities(entities)
class TeslemetryStreamSensorEntity(TeslemetryVehicleStreamEntity, RestoreSensor):
"""Base class for Teslemetry vehicle streaming sensors."""
entity_description: TeslemetryVehicleSensorEntityDescription
def __init__(
self,
data: TeslemetryVehicleData,
description: TeslemetryVehicleSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(data, description.key)
@override
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
if (sensor_data := await self.async_get_last_sensor_data()) is not None:
self._attr_native_value = sensor_data.native_value
if self.entity_description.streaming_listener is not None:
self.async_on_remove(
self.entity_description.streaming_listener(
self.vehicle.stream_vehicle, self._async_value_from_stream
)
)
def _async_value_from_stream(self, value: StateType) -> None:
"""Update the value of the entity."""
self._attr_native_value = value
self.async_write_ha_state()
class TeslemetryVehicleSensorEntity(TeslemetryVehiclePollingEntity, SensorEntity):
"""Base class for Teslemetry vehicle metric sensors."""
entity_description: TeslemetryVehicleSensorEntityDescription
def __init__(
self,
data: TeslemetryVehicleData,
description: TeslemetryVehicleSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(data, description.key)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
if self.entity_description.nullable or self._value is not None:
self._attr_available = True
self._attr_native_value = self.entity_description.polling_value_fn(
self._value
)
else:
self._attr_available = False
self._attr_native_value = None
class TeslemetryStreamTimeSensorEntity(TeslemetryVehicleStreamEntity, SensorEntity):
"""Base class for Teslemetry vehicle streaming sensors."""
entity_description: TeslemetryTimeEntityDescription
def __init__(
self,
data: TeslemetryVehicleData,
description: TeslemetryTimeEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
self._get_timestamp = ignore_variance(
func=lambda value: (
dt_util.now()
+ timedelta(**{self.entity_description.streaming_unit: value})
),
ignored_variance=timedelta(minutes=description.variance),
)
super().__init__(data, description.key)
@override
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
self.async_on_remove(
self.entity_description.streaming_listener(
self.vehicle.stream_vehicle, self._value_callback
)
)
def _value_callback(self, value: float | None) -> None:
"""Update the value of the entity."""
if value is None:
self._attr_native_value = None
else:
self._attr_native_value = self._get_timestamp(value)
self.async_write_ha_state()
class TeslemetryVehicleTimeSensorEntity(TeslemetryVehiclePollingEntity, SensorEntity):
"""Base class for Teslemetry vehicle time sensors."""
entity_description: TeslemetryTimeEntityDescription
def __init__(
self,
data: TeslemetryVehicleData,
description: TeslemetryTimeEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
self._get_timestamp = ignore_variance(
func=lambda value: dt_util.now() + timedelta(minutes=value),
ignored_variance=timedelta(minutes=description.variance),
)
super().__init__(data, description.key)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_available = isinstance(self._value, int | float) and self._value > 0
if self._attr_available:
self._attr_native_value = self._get_timestamp(self._value)
class TeslemetryEnergyLiveSensorEntity(TeslemetryEnergyLiveEntity, SensorEntity):
"""Base class for Teslemetry energy site metric sensors."""
entity_description: TeslemetryEnergySensorEntityDescription
def __init__(
self,
data: TeslemetryEnergyData,
description: TeslemetryEnergySensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(data, description.key)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_available = not self.is_none
self._attr_native_value = self.entity_description.value_fn(self._value)
class TeslemetryWallConnectorSensorEntity(TeslemetryWallConnectorEntity, SensorEntity):
"""Base class for Teslemetry energy site metric sensors."""
entity_description: TeslemetrySensorEntityDescription
def __init__(
self,
data: TeslemetryEnergyData,
din: str,
description: TeslemetrySensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(
data,
din,
description.key,
)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_native_value = self.entity_description.value_fn(self._value)
class TeslemetryEnergyInfoSensorEntity(TeslemetryEnergyInfoEntity, SensorEntity):
"""Base class for Teslemetry energy site metric sensors."""
entity_description: SensorEntityDescription
def __init__(
self,
data: TeslemetryEnergyData,
description: SensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(data, description.key)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_available = not self.is_none
self._attr_native_value = self._value
class TeslemetryEnergyHistorySensorEntity(TeslemetryEnergyHistoryEntity, SensorEntity):
"""Base class for Tesla Fleet energy site metric sensors."""
entity_description: SensorEntityDescription
def __init__(
self,
data: TeslemetryEnergyData,
description: SensorEntityDescription,
) -> None:
"""Initialize the sensor."""
self.entity_description = description
super().__init__(data, description.key)
@override
def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
self._attr_native_value = self._value
class TeslemetryCreditBalanceSensor(RestoreSensor):
"""Entity for Teslemetry Credit balance."""
_attr_has_entity_name = True
stream: TeslemetryStream
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_suggested_display_precision = 0
def __init__(self, uid: str, stream: TeslemetryStream) -> None:
"""Initialize common aspects of a Teslemetry entity."""
self._attr_translation_key = "credit_balance"
self._attr_unique_id = f"{uid}_credit_balance"
self.stream = stream
@override
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
if (sensor_data := await self.async_get_last_sensor_data()) is not None:
self._attr_native_value = sensor_data.native_value
self.async_on_remove(self.stream.listen_Balance(self._async_update))
def _async_update(self, value: int) -> None:
"""Handle updated data from the coordinator."""
self._attr_native_value = value
self.async_write_ha_state()
class TeslemetryCreditQuotaSensor(RestoreSensor):
"""Entity for Teslemetry credit quota usage."""
_attr_has_entity_name = True
stream: TeslemetryStream
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_suggested_display_precision = 1
def __init__(self, uid: str, stream: TeslemetryStream) -> None:
"""Initialize common aspects of a Teslemetry entity."""
self._attr_translation_key = "credit_quota"
self._attr_unique_id = f"{uid}_credit_quota"
self.stream = stream
@override
async def async_added_to_hass(self) -> None:
"""Handle entity which will be added."""
await super().async_added_to_hass()
if (sensor_data := await self.async_get_last_sensor_data()) is not None:
self._attr_native_value = sensor_data.native_value
self.async_on_remove(self.stream.listen_Credits(self._async_update))
def _async_update(self, credits: dict[str, Any]) -> None:
"""Handle updated data from the stream."""
quota = credits.get("quota")
if not isinstance(quota, dict):
return
fraction = quota.get("fraction")
if not isinstance(fraction, (float, int)) or isinstance(fraction, bool):
return
self._attr_native_value = fraction * 100
self.async_write_ha_state()