From 6b61f73c0b32797c1951d8122becc07907f2c431 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Wed, 30 Sep 2026 20:07:05 +1000 Subject: [PATCH] Fix Teslemetry climate keeper preset for polling vehicles (#183681) --- .../components/teslemetry/climate.py | 11 +++++- .../teslemetry/fixtures/vehicle_data.json | 2 +- .../snapshots/test_diagnostics.ambr | 2 +- tests/components/teslemetry/test_climate.py | 35 +++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/teslemetry/climate.py b/homeassistant/components/teslemetry/climate.py index 18cf92c540e4..6b7b3fbdef31 100644 --- a/homeassistant/components/teslemetry/climate.py +++ b/homeassistant/components/teslemetry/climate.py @@ -50,6 +50,12 @@ PRESET_MODES = { "Dog": "dog", "Party": "camp", } +POLLING_PRESET_MODES = { + "off": "off", + "on": "keep", + "dog": "dog", + "camp": "camp", +} PARALLEL_UPDATES = 0 @@ -233,7 +239,10 @@ class TeslemetryVehiclePollingClimateEntity( self._attr_current_temperature = self.get("climate_state_inside_temp") self._attr_target_temperature = self.get(f"climate_state_{self.key}_setting") - self._attr_preset_mode = self.get("climate_state_climate_keeper_mode") + keeper_mode = self.get("climate_state_climate_keeper_mode") + self._attr_preset_mode = ( + POLLING_PRESET_MODES.get(keeper_mode) if keeper_mode else None + ) if self.get("climate_state_bioweapon_mode"): self._attr_fan_mode = "bioweapon" else: diff --git a/tests/components/teslemetry/fixtures/vehicle_data.json b/tests/components/teslemetry/fixtures/vehicle_data.json index a39aef23d56e..8396ac74d23c 100644 --- a/tests/components/teslemetry/fixtures/vehicle_data.json +++ b/tests/components/teslemetry/fixtures/vehicle_data.json @@ -80,7 +80,7 @@ "battery_heater_no_power": null, "cabin_overheat_protection": "On", "cabin_overheat_protection_actively_cooling": false, - "climate_keeper_mode": "keep", + "climate_keeper_mode": "on", "cop_activation_temperature": "High", "defrost_mode": 0, "driver_temp_setting": 22, diff --git a/tests/components/teslemetry/snapshots/test_diagnostics.ambr b/tests/components/teslemetry/snapshots/test_diagnostics.ambr index 2db8e644168b..7903b73bd571 100644 --- a/tests/components/teslemetry/snapshots/test_diagnostics.ambr +++ b/tests/components/teslemetry/snapshots/test_diagnostics.ambr @@ -392,7 +392,7 @@ 'climate_state_battery_heater_no_power': None, 'climate_state_cabin_overheat_protection': 'On', 'climate_state_cabin_overheat_protection_actively_cooling': False, - 'climate_state_climate_keeper_mode': 'keep', + 'climate_state_climate_keeper_mode': 'on', 'climate_state_cop_activation_temperature': 'High', 'climate_state_defrost_mode': 0, 'climate_state_driver_temp_setting': 22, diff --git a/tests/components/teslemetry/test_climate.py b/tests/components/teslemetry/test_climate.py index f21ad283d6f7..376185090b8c 100644 --- a/tests/components/teslemetry/test_climate.py +++ b/tests/components/teslemetry/test_climate.py @@ -234,6 +234,41 @@ async def test_climate_state_unknown( assert hass.states.get("climate.test_climate").state == STATE_UNKNOWN +@pytest.mark.parametrize( + ("keeper_mode", "preset_mode"), + [ + pytest.param("on", "keep", id="on"), + pytest.param("dog", "dog", id="dog"), + pytest.param("camp", "camp", id="camp"), + pytest.param("keep", None, id="unmapped"), + pytest.param(None, None, id="unknown"), + ], +) +async def test_climate_polling_keeper_mode( + hass: HomeAssistant, + mock_metadata: AsyncMock, + mock_vehicle_data: AsyncMock, + keeper_mode: str | None, + preset_mode: str | None, +) -> None: + """Test that a polling vehicle maps Tesla's climate keeper mode to a preset.""" + + metadata = deepcopy(METADATA) + metadata["vehicles"]["LRW3F7EK4NC700000"]["polling"] = True + mock_metadata.return_value = metadata + + data = deepcopy(VEHICLE_DATA_ALT) + data["response"]["climate_state"]["climate_keeper_mode"] = keeper_mode + mock_vehicle_data.return_value = data + + await setup_platform(hass, [Platform.CLIMATE]) + + assert ( + hass.states.get("climate.test_climate").attributes[ATTR_PRESET_MODE] + == preset_mode + ) + + async def test_invalid_error(hass: HomeAssistant, snapshot: SnapshotAssertion) -> None: """Tests service error is handled."""