Change Climate set temp action for incorrect feature will raise (#126692)

* Change Climate set temp action for incorrect feature will raise

* Fix some tests

* Fix review comments

* Fix tesla_fleet

* Fix tests

* Fix review comment
This commit is contained in:
G Johansson 2024-09-25 21:16:14 +02:00 committed by GitHub
parent 9afd270111
commit c6a1b9fc39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 85 additions and 166 deletions

View file

@ -965,46 +965,18 @@ async def async_service_temperature_set(
ATTR_TEMPERATURE in service_call.data
and not entity.supported_features & ClimateEntityFeature.TARGET_TEMPERATURE
):
# Warning implemented in 2024.10 and will be changed to raising
# a ServiceValidationError in 2025.4
report_issue = async_suggest_report_issue(
entity.hass,
integration_domain=entity.platform.platform_name,
module=type(entity).__module__,
)
_LOGGER.warning(
(
"%s::%s set_temperature action was used with temperature but the entity does not "
"implement the ClimateEntityFeature.TARGET_TEMPERATURE feature. "
"This will stop working in 2025.4 and raise an error instead. "
"Please %s"
),
entity.platform.platform_name,
entity.__class__.__name__,
report_issue,
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="missing_target_temperature_entity_feature",
)
if (
ATTR_TARGET_TEMP_LOW in service_call.data
and not entity.supported_features
& ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
):
# Warning implemented in 2024.10 and will be changed to raising
# a ServiceValidationError in 2025.4
report_issue = async_suggest_report_issue(
entity.hass,
integration_domain=entity.platform.platform_name,
module=type(entity).__module__,
)
_LOGGER.warning(
(
"%s::%s set_temperature action was used with target_temp_low but the entity does not "
"implement the ClimateEntityFeature.TARGET_TEMPERATURE_RANGE feature. "
"This will stop working in 2025.4 and raise an error instead. "
"Please %s"
),
entity.platform.platform_name,
entity.__class__.__name__,
report_issue,
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="missing_target_temperature_range_entity_feature",
)
hass = entity.hass

View file

@ -275,6 +275,12 @@
},
"humidity_out_of_range": {
"message": "Provided humidity {humidity} is not valid. Accepted range is {min_humidity} to {max_humidity}."
},
"missing_target_temperature_entity_feature": {
"message": "Set temperature action was used with the target temperature parameter but the entity does not support it."
},
"missing_target_temperature_range_entity_feature": {
"message": "Set temperature action was used with the target temperature low/high parameter but the entity does not support it."
}
}
}

View file

@ -290,40 +290,34 @@ async def test_temperature_features_is_valid(
await hass.config_entries.async_setup(register_test_integration.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test_temp",
"temperature": 20,
},
blocking=True,
)
assert (
"MockClimateTempEntity set_temperature action was used "
"with temperature but the entity does not "
"implement the ClimateEntityFeature.TARGET_TEMPERATURE feature. "
"This will stop working in 2025.4 and raise an error instead. "
"Please"
) in caplog.text
with pytest.raises(
ServiceValidationError,
match="Set temperature action was used with the target temperature parameter but the entity does not support it",
):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test_temp",
"temperature": 20,
},
blocking=True,
)
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test_range",
"target_temp_low": 20,
"target_temp_high": 25,
},
blocking=True,
)
assert (
"MockClimateTempRangeEntity set_temperature action was used with "
"target_temp_low but the entity does not "
"implement the ClimateEntityFeature.TARGET_TEMPERATURE_RANGE feature. "
"This will stop working in 2025.4 and raise an error instead. "
"Please"
) in caplog.text
with pytest.raises(
ServiceValidationError,
match="Set temperature action was used with the target temperature low/high parameter but the entity does not support it",
):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_TEMPERATURE,
{
"entity_id": "climate.test_range",
"target_temp_low": 20,
"target_temp_high": 25,
},
blocking=True,
)
async def test_mode_validation(

View file

@ -259,7 +259,7 @@ async def test_climate_device_without_cooling_support(
# Service set temperature without providing temperature attribute
with pytest.raises(ValueError):
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,

View file

@ -13,6 +13,7 @@ from aioesphomeapi import (
ClimateState,
ClimateSwingMode,
)
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.climate import (
@ -41,6 +42,7 @@ from homeassistant.components.climate import (
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
async def test_climate_entity(
@ -54,7 +56,6 @@ async def test_climate_entity(
name="my climate",
unique_id="my_climate",
supports_current_temperature=True,
supports_two_point_target_temperature=True,
supports_action=True,
visual_min_temperature=10.0,
visual_max_temperature=30.0,
@ -134,14 +135,13 @@ async def test_climate_entity_with_step_and_two_point(
assert state is not None
assert state.state == HVACMode.COOL
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_TEMPERATURE: 25},
blocking=True,
)
mock_client.climate_command.assert_has_calls([call(key=1, target_temperature=25.0)])
mock_client.climate_command.reset_mock()
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_TEMPERATURE: 25},
blocking=True,
)
await hass.services.async_call(
CLIMATE_DOMAIN,
@ -213,38 +213,34 @@ async def test_climate_entity_with_step_and_target_temp(
assert state is not None
assert state.state == HVACMode.COOL
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: "climate.test_myclimate", ATTR_TEMPERATURE: 25},
blocking=True,
)
mock_client.climate_command.assert_has_calls([call(key=1, target_temperature=25.0)])
mock_client.climate_command.reset_mock()
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.test_myclimate",
ATTR_HVAC_MODE: HVACMode.AUTO,
ATTR_TARGET_TEMP_LOW: 20,
ATTR_TARGET_TEMP_HIGH: 30,
ATTR_TEMPERATURE: 25,
},
blocking=True,
)
mock_client.climate_command.assert_has_calls(
[
call(
key=1,
mode=ClimateMode.AUTO,
target_temperature_low=20.0,
target_temperature_high=30.0,
)
]
[call(key=1, mode=ClimateMode.AUTO, target_temperature=25.0)]
)
mock_client.climate_command.reset_mock()
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.test_myclimate",
ATTR_HVAC_MODE: HVACMode.AUTO,
ATTR_TARGET_TEMP_LOW: 20,
ATTR_TARGET_TEMP_HIGH: 30,
},
blocking=True,
)
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,

View file

@ -15,8 +15,6 @@ from homeassistant.components.climate import (
ATTR_MIN_TEMP,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
DOMAIN as CLIMATE_DOMAIN,
PRESET_COMFORT,
PRESET_ECO,
@ -290,13 +288,6 @@ async def test_update_error(hass: HomeAssistant, fritz: Mock) -> None:
},
[call(23)],
),
(
{
ATTR_TARGET_TEMP_HIGH: 16,
ATTR_TARGET_TEMP_LOW: 10,
},
[],
),
],
)
async def test_set_temperature(

View file

@ -141,13 +141,6 @@ async def test_hmip_heating_group_heat(
ha_state = hass.states.get(entity_id)
assert ha_state.attributes[ATTR_PRESET_MODE] == "STD"
# Not required for hmip, but a possibility to send no temperature.
await hass.services.async_call(
"climate",
"set_temperature",
{"entity_id": entity_id, "target_temp_low": 10, "target_temp_high": 10},
blocking=True,
)
# No new service call should be in mock_calls.
assert len(hmip_device.mock_calls) == service_call_counter + 12
# Only fire event from last async_manipulate_test_data available.

View file

@ -5,6 +5,7 @@ from unittest.mock import patch
from pypck.inputs import ModStatusVar, Unknown
from pypck.lcn_addr import LcnAddr
from pypck.lcn_defs import Var, VarUnit, VarValue
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.climate import (
@ -25,6 +26,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import entity_registry as er
from .conftest import MockConfigEntry, MockModuleConnection, init_integration
@ -140,16 +142,17 @@ async def test_set_temperature(hass: HomeAssistant, entry: MockConfigEntry) -> N
# wrong temperature set via service call with high/low attributes
var_abs.return_value = False
await hass.services.async_call(
DOMAIN_CLIMATE,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.climate1",
ATTR_TARGET_TEMP_LOW: 24.5,
ATTR_TARGET_TEMP_HIGH: 25.5,
},
blocking=True,
)
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
DOMAIN_CLIMATE,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: "climate.climate1",
ATTR_TARGET_TEMP_LOW: 24.5,
ATTR_TARGET_TEMP_HIGH: 25.5,
},
blocking=True,
)
var_abs.assert_not_awaited()

View file

@ -216,7 +216,7 @@ async def test_thermostat_set_no_temperature(
hass: HomeAssistant, cube: MaxCube, thermostat: MaxThermostat
) -> None:
"""Set hvac mode to heat."""
with pytest.raises(ValueError):
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,

View file

@ -13,8 +13,6 @@ from homeassistant.components.climate import (
ATTR_HVAC_ACTION,
ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
DOMAIN as CLIMATE_DOMAIN,
PRESET_NONE,
SERVICE_SET_HVAC_MODE,
@ -138,19 +136,6 @@ async def test_climate_set_temperature(
assert state.state == HVACMode.OFF
assert state.attributes[ATTR_TEMPERATURE] == 4
# Test set temperature without target temperature
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_TARGET_TEMP_LOW: 20,
ATTR_TARGET_TEMP_HIGH: 30,
},
blocking=True,
)
mock_block_device.http_request.assert_not_called()
# Test set temperature
await hass.services.async_call(
CLIMATE_DOMAIN,
@ -684,19 +669,6 @@ async def test_rpc_climate_set_temperature(
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 23
# test set temperature without target temperature
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: entity_id,
ATTR_TARGET_TEMP_LOW: 20,
ATTR_TARGET_TEMP_HIGH: 30,
},
blocking=True,
)
mock_rpc_device.call_rpc.assert_not_called()
monkeypatch.setitem(mock_rpc_device.status["thermostat:0"], "target_C", 28)
await hass.services.async_call(
CLIMATE_DOMAIN,

View file

@ -98,6 +98,10 @@ async def test_climate_temperature(
await init_integration(hass)
assert mock_bridge
monkeypatch.setattr(DEVICE, "mode", ThermostatMode.HEAT)
mock_bridge.mock_callbacks([DEVICE])
await hass.async_block_till_done()
# Test initial target temperature
state = hass.states.get(ENTITY_ID)
assert state.attributes["temperature"] == 23
@ -126,7 +130,7 @@ async def test_climate_temperature(
with patch(
"homeassistant.components.switcher_kis.climate.SwitcherType2Api.control_breeze_device",
) as mock_control_device:
with pytest.raises(ValueError):
with pytest.raises(ServiceValidationError):
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,

View file

@ -436,7 +436,8 @@ async def test_climate_notemp(
await setup_platform(hass, normal_config_entry, [Platform.CLIMATE])
with pytest.raises(
ServiceValidationError, match="Temperature is required for this action"
ServiceValidationError,
match="Set temperature action was used with the target temperature low/high parameter but the entity does not support it",
):
await hass.services.async_call(
CLIMATE_DOMAIN,

View file

@ -10,8 +10,6 @@ from tesla_fleet_api.exceptions import InvalidCommand, VehicleOffline
from homeassistant.components.climate import (
ATTR_HVAC_MODE,
ATTR_PRESET_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
ATTR_TEMPERATURE,
DOMAIN as CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
@ -175,17 +173,6 @@ async def test_climate(
state = hass.states.get(entity_id)
assert state.state == HVACMode.COOL
# Set Temp do nothing
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{
ATTR_ENTITY_ID: [entity_id],
ATTR_TARGET_TEMP_HIGH: 30,
ATTR_TARGET_TEMP_LOW: 30,
},
blocking=True,
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_TEMPERATURE] == 40
assert state.state == HVACMode.COOL