diff --git a/homeassistant/components/teslemetry/climate.py b/homeassistant/components/teslemetry/climate.py index ab6442765317..4a5398e87d78 100644 --- a/homeassistant/components/teslemetry/climate.py +++ b/homeassistant/components/teslemetry/climate.py @@ -23,12 +23,11 @@ from homeassistant.const import ( UnitOfTemperature, ) from homeassistant.core import HomeAssistant -from homeassistant.exceptions import ServiceValidationError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity from . import TeslemetryConfigEntry -from .const import DOMAIN, TeslemetryClimateSide +from .const import TeslemetryClimateSide from .entity import ( TeslemetryRootEntity, TeslemetryVehiclePollingEntity, @@ -426,15 +425,12 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryRootEntity, ClimateEntit """Set the climate temperature.""" if temp := kwargs.get(ATTR_TEMPERATURE): - if (cop_mode := COP_TEMPERATURES.get(temp)) is None: - raise ServiceValidationError( - translation_domain=DOMAIN, - translation_key="invalid_cop_temp", - ) + # Temperatures converted from Fahrenheit rarely hit a level exactly + level = min(COP_TEMPERATURES, key=lambda t: abs(t - temp)) self.raise_for_scope(Scope.VEHICLE_CMDS) - await handle_vehicle_command(self.api.set_cop_temp(cop_mode)) - self._attr_target_temperature = temp + await handle_vehicle_command(self.api.set_cop_temp(COP_TEMPERATURES[level])) + self._attr_target_temperature = level if mode := kwargs.get(ATTR_HVAC_MODE): # Set HVAC mode will call write_ha_state diff --git a/homeassistant/components/teslemetry/strings.json b/homeassistant/components/teslemetry/strings.json index c68cf1e1df37..6bbde906044f 100644 --- a/homeassistant/components/teslemetry/strings.json +++ b/homeassistant/components/teslemetry/strings.json @@ -1244,9 +1244,6 @@ "command_reason": { "message": "Command was rejected: {reason}" }, - "invalid_cop_temp": { - "message": "Cabin overheat protection does not support that temperature" - }, "missing_scope": { "message": "Missing required scope: {scope}" }, diff --git a/tests/components/teslemetry/test_climate.py b/tests/components/teslemetry/test_climate.py index 83cfbc754fc9..493b231089cc 100644 --- a/tests/components/teslemetry/test_climate.py +++ b/tests/components/teslemetry/test_climate.py @@ -31,6 +31,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import entity_registry as er +from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM from . import assert_entities, reload_platform, setup_platform from .const import ( @@ -185,18 +186,47 @@ async def test_climate( assert state.attributes[ATTR_TEMPERATURE] == 40 assert state.state == HVACMode.COOL - # pytest raises ServiceValidationError - with pytest.raises( - ServiceValidationError, - match="Cabin overheat protection does not support that temperature", - ): - # Invalid Temp + # Set Temp between levels snaps to the nearest level + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_TEMPERATURE, + {ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 34}, + blocking=True, + ) + state = hass.states.get(entity_id) + assert state.attributes[ATTR_TEMPERATURE] == 35 + + +@pytest.mark.parametrize( + ("temperature", "cop_temp"), + [ + pytest.param(86, CabinOverheatProtectionTemp.LOW, id="low"), + pytest.param(91, CabinOverheatProtectionTemp.MEDIUM, id="medium"), + pytest.param(104, CabinOverheatProtectionTemp.HIGH, id="high"), + ], +) +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "mock_legacy") +async def test_cop_set_temperature_fahrenheit( + hass: HomeAssistant, + temperature: int, + cop_temp: CabinOverheatProtectionTemp, +) -> None: + """Tests a Fahrenheit temperature is snapped to the nearest COP level.""" + hass.config.units = US_CUSTOMARY_SYSTEM + 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: 34}, + {ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: temperature}, blocking=True, ) + mock_set_cop_temp.assert_called_once_with(cop_temp) @pytest.mark.usefixtures("entity_registry_enabled_by_default")