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:
parent
9afd270111
commit
c6a1b9fc39
13 changed files with 85 additions and 166 deletions
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue