From ec82dee53dbaaa2a6e263542484bd3cc44700b3d Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Mon, 7 Sep 2026 18:14:16 +1000 Subject: [PATCH] Use PressureConverter for Teslemetry TPMS streaming conversion (#181508) --- homeassistant/components/teslemetry/sensor.py | 36 ++++++++++++--- tests/components/teslemetry/test_sensor.py | 46 +++++++++++++++---- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/teslemetry/sensor.py b/homeassistant/components/teslemetry/sensor.py index 3feffe7139eb..5a9c88adb518 100644 --- a/homeassistant/components/teslemetry/sensor.py +++ b/homeassistant/components/teslemetry/sensor.py @@ -34,6 +34,7 @@ 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.unit_conversion import PressureConverter from homeassistant.util.variance import ignore_variance from . import TeslemetryConfigEntry @@ -50,9 +51,6 @@ 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" @@ -403,7 +401,13 @@ VEHICLE_DESCRIPTIONS: tuple[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) + lambda x: ( + callback(None) + if x is None + else callback( + PressureConverter.convert(x, UnitOfPressure.ATM, UnitOfPressure.BAR) + ) + ) ), state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPressure.BAR, @@ -417,7 +421,13 @@ VEHICLE_DESCRIPTIONS: tuple[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) + lambda x: ( + callback(None) + if x is None + else callback( + PressureConverter.convert(x, UnitOfPressure.ATM, UnitOfPressure.BAR) + ) + ) ), state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPressure.BAR, @@ -431,7 +441,13 @@ VEHICLE_DESCRIPTIONS: tuple[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) + lambda x: ( + callback(None) + if x is None + else callback( + PressureConverter.convert(x, UnitOfPressure.ATM, UnitOfPressure.BAR) + ) + ) ), state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPressure.BAR, @@ -445,7 +461,13 @@ VEHICLE_DESCRIPTIONS: tuple[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) + lambda x: ( + callback(None) + if x is None + else callback( + PressureConverter.convert(x, UnitOfPressure.ATM, UnitOfPressure.BAR) + ) + ) ), state_class=SensorStateClass.MEASUREMENT, native_unit_of_measurement=UnitOfPressure.BAR, diff --git a/tests/components/teslemetry/test_sensor.py b/tests/components/teslemetry/test_sensor.py index bb2c23fefe33..3d5ffeda3628 100644 --- a/tests/components/teslemetry/test_sensor.py +++ b/tests/components/teslemetry/test_sensor.py @@ -16,11 +16,9 @@ from homeassistant.const import ( STATE_UNKNOWN, EntityCategory, Platform, - UnitOfPressure, ) from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from homeassistant.util.unit_conversion import PressureConverter from . import assert_entities, assert_entities_alt, setup_platform from .const import ( @@ -326,29 +324,25 @@ async def test_hw4_mileage_sensors_gating( Signal.TPMS_PRESSURE_FL, "sensor.test_tire_pressure_front_left", 2.7, - # 2.7 atm independently hand-converted to bar (2.7 * 1.01325 = 2.735775) - PressureConverter.convert(2.735775, UnitOfPressure.BAR, UnitOfPressure.PSI), + 39.679063381059, ), ( Signal.TPMS_PRESSURE_FR, "sensor.test_tire_pressure_front_right", 2.7, - # 2.7 atm independently hand-converted to bar (2.7 * 1.01325 = 2.735775) - PressureConverter.convert(2.735775, UnitOfPressure.BAR, UnitOfPressure.PSI), + 39.679063381059, ), ( Signal.TPMS_PRESSURE_RL, "sensor.test_tire_pressure_rear_left", 2.7, - # 2.7 atm independently hand-converted to bar (2.7 * 1.01325 = 2.735775) - PressureConverter.convert(2.735775, UnitOfPressure.BAR, UnitOfPressure.PSI), + 39.679063381059, ), ( Signal.TPMS_PRESSURE_RR, "sensor.test_tire_pressure_rear_right", 2.7, - # 2.7 atm independently hand-converted to bar (2.7 * 1.01325 = 2.735775) - PressureConverter.convert(2.735775, UnitOfPressure.BAR, UnitOfPressure.PSI), + 39.679063381059, ), ( Signal.ISOLATION_RESISTANCE, @@ -386,6 +380,38 @@ async def test_sensors_streaming_unit_conversion( assert float(state.state) == pytest.approx(expected_state) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_sensors_streaming_tpms_none_clears_state( + hass: HomeAssistant, + mock_vehicle_data: AsyncMock, + mock_add_listener: AsyncMock, +) -> None: + """A None streamed TPMS pressure must clear the entity, not pass through the converter.""" + entity_id = "sensor.test_tire_pressure_front_left" + await setup_platform(hass, [Platform.SENSOR]) + vin = VEHICLE_DATA_ALT["response"]["vin"] + + mock_add_listener.send( + { + "vin": vin, + "data": {Signal.TPMS_PRESSURE_FL: 2.7}, + "createdAt": "2024-10-04T10:45:17.537Z", + } + ) + await hass.async_block_till_done() + assert hass.states.get(entity_id).state != STATE_UNKNOWN + + mock_add_listener.send( + { + "vin": vin, + "data": {Signal.TPMS_PRESSURE_FL: None}, + "createdAt": "2024-10-04T10:45:18.537Z", + } + ) + await hass.async_block_till_done() + assert hass.states.get(entity_id).state == STATE_UNKNOWN + + @pytest.mark.parametrize( ("key", "signal", "raw_value", "state"), [