diff --git a/homeassistant/components/teslemetry/climate.py b/homeassistant/components/teslemetry/climate.py index 917ada942230..c5676f76484c 100644 --- a/homeassistant/components/teslemetry/climate.py +++ b/homeassistant/components/teslemetry/climate.py @@ -62,6 +62,8 @@ async def async_setup_entry( ) -> None: """Set up the Teslemetry Climate platform from a config entry.""" + # Streaming vehicles are never polled, so their vehicle_config comes from metadata + vehicles_metadata = entry.runtime_data.metadata_coordinator.data["vehicles"] async_add_entities( chain( ( @@ -80,7 +82,13 @@ async def async_setup_entry( ) if vehicle.poll or not firmware_at_least(vehicle.firmware, "2024.44.25") else TeslemetryStreamingCabinOverheatProtectionEntity( - vehicle, entry.runtime_data.scopes + vehicle, + entry.runtime_data.scopes, + bool( + vehicles_metadata[vehicle.vin] + .get("config", {}) + .get("cop_user_set_temp_supported") + ), ) for vehicle in entry.runtime_data.vehicles ), @@ -507,6 +515,7 @@ class TeslemetryStreamingCabinOverheatProtectionEntity( self, data: TeslemetryVehicleData, scopes: list[Scope], + cop_temp_supported: bool, ) -> None: """Initialize the climate.""" @@ -523,7 +532,7 @@ class TeslemetryStreamingCabinOverheatProtectionEntity( self._attr_supported_features = ( ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF ) - if data.coordinator.data.get("vehicle_config_cop_user_set_temp_supported"): + if cop_temp_supported: self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE # Scopes diff --git a/tests/components/teslemetry/test_climate.py b/tests/components/teslemetry/test_climate.py index 68342935a822..56051ce5b042 100644 --- a/tests/components/teslemetry/test_climate.py +++ b/tests/components/teslemetry/test_climate.py @@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch import pytest from syrupy.assertion import SnapshotAssertion +from tesla_fleet_api.const import CabinOverheatProtectionTemp from tesla_fleet_api.exceptions import InvalidCommand from teslemetry_stream import Signal @@ -18,9 +19,15 @@ from homeassistant.components.climate import ( SERVICE_SET_TEMPERATURE, SERVICE_TURN_OFF, SERVICE_TURN_ON, + ClimateEntityFeature, HVACMode, ) -from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform +from homeassistant.const import ( + ATTR_ENTITY_ID, + ATTR_SUPPORTED_FEATURES, + STATE_UNKNOWN, + Platform, +) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import entity_registry as er @@ -29,6 +36,7 @@ from . import assert_entities, reload_platform, setup_platform from .const import ( COMMAND_ERRORS, COMMAND_IGNORED_REASON, + COMMAND_OK, METADATA, METADATA_NOSCOPE, VEHICLE_DATA_ALT, @@ -370,3 +378,74 @@ async def test_select_streaming( "climate.test_cabin_overheat_protection", ): assert hass.states.get(entity_id) == snapshot(name=entity_id) + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +@pytest.mark.parametrize( + ("config", "supported_features"), + [ + pytest.param( + {"cop_user_set_temp_supported": True}, + ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.TURN_ON + | ClimateEntityFeature.TURN_OFF, + id="supported", + ), + pytest.param( + {"cop_user_set_temp_supported": False}, + ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF, + id="unsupported", + ), + pytest.param( + {}, + ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF, + id="missing", + ), + ], +) +async def test_cabin_overheat_protection_streaming_features( + hass: HomeAssistant, + mock_metadata: AsyncMock, + config: dict[str, bool], + supported_features: ClimateEntityFeature, +) -> None: + """Test streaming cabin overheat protection features come from metadata config.""" + + metadata = deepcopy(METADATA) + metadata["vehicles"]["LRW3F7EK4NC700000"]["config"] = config + mock_metadata.return_value = metadata + + await setup_platform(hass, [Platform.CLIMATE]) + + state = hass.states.get("climate.test_cabin_overheat_protection") + assert state.attributes[ATTR_SUPPORTED_FEATURES] == supported_features + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_cabin_overheat_protection_streaming_set_temperature( + hass: HomeAssistant, + mock_metadata: AsyncMock, +) -> None: + """Test setting the streaming cabin overheat protection temperature.""" + + metadata = deepcopy(METADATA) + metadata["vehicles"]["LRW3F7EK4NC700000"]["config"] = { + "cop_user_set_temp_supported": True + } + mock_metadata.return_value = metadata + + await setup_platform(hass, [Platform.CLIMATE]) + entity_id = "climate.test_cabin_overheat_protection" + + with patch( + "tesla_fleet_api.teslemetry.Vehicle.set_cop_temp", + return_value=COMMAND_OK, + ) as mock_set_cop_temp: + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 35}, + blocking=True, + ) + mock_set_cop_temp.assert_called_once_with(CabinOverheatProtectionTemp.MEDIUM) + assert hass.states.get(entity_id).attributes[ATTR_TEMPERATURE] == 35