Add validation to climate hvac mode (#125178)

* Add validation to climate hvac mode

* Make softer

* Remove string

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
G Johansson 2024-09-08 14:24:12 +02:00 committed by GitHub
parent 2ef37f01b1
commit c2d5696b5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 58 additions and 13 deletions

View file

@ -175,7 +175,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
component.async_register_entity_service(
SERVICE_SET_HVAC_MODE,
{vol.Required(ATTR_HVAC_MODE): vol.Coerce(HVACMode)},
"async_set_hvac_mode",
"async_handle_set_hvac_mode_service",
)
component.async_register_entity_service(
SERVICE_SET_PRESET_MODE,
@ -694,20 +694,35 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@callback
def _valid_mode_or_raise(
self,
mode_type: Literal["preset", "swing", "fan"],
mode: str,
modes: list[str] | None,
mode_type: Literal["preset", "swing", "fan", "hvac"],
mode: str | HVACMode,
modes: list[str] | list[HVACMode] | None,
) -> None:
"""Raise ServiceValidationError on invalid modes."""
if modes and mode in modes:
return
modes_str: str = ", ".join(modes) if modes else ""
if mode_type == "preset":
translation_key = "not_valid_preset_mode"
elif mode_type == "swing":
translation_key = "not_valid_swing_mode"
elif mode_type == "fan":
translation_key = "not_valid_fan_mode"
translation_key = f"not_valid_{mode_type}_mode"
if mode_type == "hvac":
report_issue = async_suggest_report_issue(
self.hass,
integration_domain=self.platform.platform_name,
module=type(self).__module__,
)
_LOGGER.warning(
(
"%s::%s sets the hvac_mode %s which is not "
"valid for this entity with modes: %s. "
"This will stop working in 2025.3 and raise an error instead. "
"Please %s"
),
self.platform.platform_name,
self.__class__.__name__,
mode,
modes_str,
report_issue,
)
return
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key=translation_key,
@ -749,6 +764,12 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
"""Set new target fan mode."""
await self.hass.async_add_executor_job(self.set_fan_mode, fan_mode)
@final
async def async_handle_set_hvac_mode_service(self, hvac_mode: HVACMode) -> None:
"""Validate and set new preset mode."""
self._valid_mode_or_raise("hvac", hvac_mode, self.hvac_modes)
await self.async_set_hvac_mode(hvac_mode)
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
raise NotImplementedError

View file

@ -27,6 +27,7 @@ from homeassistant.components.climate.const import (
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
SERVICE_SET_FAN_MODE,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_PRESET_MODE,
SERVICE_SET_SWING_MODE,
SERVICE_SET_TEMPERATURE,
@ -138,6 +139,10 @@ class MockClimateEntity(MockEntity, ClimateEntity):
"""Set swing mode."""
self._attr_swing_mode = swing_mode
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
self._attr_hvac_mode = hvac_mode
class MockClimateEntityTestMethods(MockClimateEntity):
"""Mock Climate device."""
@ -237,10 +242,12 @@ def test_deprecated_current_constants(
)
async def test_preset_mode_validation(
hass: HomeAssistant, register_test_integration: MockConfigEntry
async def test_mode_validation(
hass: HomeAssistant,
register_test_integration: MockConfigEntry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test mode validation for fan, swing and preset."""
"""Test mode validation for hvac_mode, fan, swing and preset."""
climate_entity = MockClimateEntity(name="test", entity_id="climate.test")
setup_test_component_platform(
@ -250,6 +257,7 @@ async def test_preset_mode_validation(
await hass.async_block_till_done()
state = hass.states.get("climate.test")
assert state.state == "heat"
assert state.attributes.get(ATTR_PRESET_MODE) == "home"
assert state.attributes.get(ATTR_FAN_MODE) == "auto"
assert state.attributes.get(ATTR_SWING_MODE) == "auto"
@ -286,6 +294,22 @@ async def test_preset_mode_validation(
assert state.attributes.get(ATTR_FAN_MODE) == "off"
assert state.attributes.get(ATTR_SWING_MODE) == "off"
await hass.services.async_call(
DOMAIN,
SERVICE_SET_HVAC_MODE,
{
"entity_id": "climate.test",
"hvac_mode": "auto",
},
blocking=True,
)
assert (
"MockClimateEntity sets the hvac_mode auto which is not valid "
"for this entity with modes: off, heat. This will stop working "
"in 2025.3 and raise an error instead. Please" in caplog.text
)
with pytest.raises(
ServiceValidationError,
match="Preset mode invalid is not valid. Valid preset modes are: home, away",