Add validation to set_humidity action in humidifier (#125863)

This commit is contained in:
G Johansson 2024-09-19 09:50:47 +02:00 committed by GitHub
parent dd10a833db
commit 4d63bf473d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 191 additions and 4 deletions

View File

@ -18,7 +18,8 @@ from homeassistant.const import (
SERVICE_TURN_ON,
STATE_ON,
)
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.deprecation import (
all_with_deprecated_constants,
@ -45,7 +46,13 @@ from .const import ( # noqa: F401
DOMAIN,
MODE_AUTO,
MODE_AWAY,
MODE_BABY,
MODE_BOOST,
MODE_COMFORT,
MODE_ECO,
MODE_HOME,
MODE_NORMAL,
MODE_SLEEP,
SERVICE_SET_HUMIDITY,
SERVICE_SET_MODE,
HumidifierAction,
@ -108,7 +115,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
vol.Coerce(int), vol.Range(min=0, max=100)
)
},
"async_set_humidity",
async_service_humidity_set,
)
return True
@ -281,6 +288,33 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
return features
async def async_service_humidity_set(
entity: HumidifierEntity, service_call: ServiceCall
) -> None:
"""Handle set humidity service."""
humidity = service_call.data[ATTR_HUMIDITY]
min_humidity = entity.min_humidity
max_humidity = entity.max_humidity
_LOGGER.debug(
"Check valid humidity %d in range %d - %d",
humidity,
min_humidity,
max_humidity,
)
if humidity < min_humidity or humidity > max_humidity:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="humidity_out_of_range",
translation_placeholders={
"humidity": str(humidity),
"min_humidity": str(min_humidity),
"max_humidity": str(max_humidity),
},
)
await entity.async_set_humidity(humidity)
# As we import deprecated constants from the const module, we need to add these two functions
# otherwise this module will be logged for using deprecated constants and not the custom component
# These can be removed if no deprecated constant are in this module anymore

View File

@ -115,5 +115,10 @@
"name": "[%key:common::action::toggle%]",
"description": "Toggles the humidifier on/off."
}
},
"exceptions": {
"humidity_out_of_range": {
"message": "Provided humidity {humidity} is not valid. Accepted range is {min_humidity} to {max_humidity}."
}
}
}

View File

@ -0,0 +1,69 @@
"""Fixtures for Humidifier platform tests."""
from collections.abc import Generator
import pytest
from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN
from homeassistant.config_entries import ConfigEntry, ConfigFlow
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from tests.common import (
MockConfigEntry,
MockModule,
mock_config_flow,
mock_integration,
mock_platform,
)
class MockFlow(ConfigFlow):
"""Test flow."""
@pytest.fixture
def config_flow_fixture(hass: HomeAssistant) -> Generator[None]:
"""Mock config flow."""
mock_platform(hass, "test.config_flow")
with mock_config_flow("test", MockFlow):
yield
@pytest.fixture
def register_test_integration(
hass: HomeAssistant, config_flow_fixture: None
) -> Generator:
"""Provide a mocked integration for tests."""
config_entry = MockConfigEntry(domain="test")
config_entry.add_to_hass(hass)
async def help_async_setup_entry_init(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Set up test config entry."""
await hass.config_entries.async_forward_entry_setups(
config_entry, [HUMIDIFIER_DOMAIN]
)
return True
async def help_async_unload_entry(
hass: HomeAssistant, config_entry: ConfigEntry
) -> bool:
"""Unload test config emntry."""
return await hass.config_entries.async_unload_platforms(
config_entry, [Platform.HUMIDIFIER]
)
mock_integration(
hass,
MockModule(
"test",
async_setup_entry=help_async_setup_entry_init,
async_unload_entry=help_async_unload_entry,
),
)
return config_entry

View File

@ -8,16 +8,28 @@ import pytest
from homeassistant.components import humidifier
from homeassistant.components.humidifier import (
ATTR_HUMIDITY,
ATTR_MODE,
DOMAIN as HUMIDIFIER_DOMAIN,
MODE_ECO,
MODE_NORMAL,
SERVICE_SET_HUMIDITY,
HumidifierEntity,
HumidifierEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from tests.common import help_test_all, import_and_test_deprecated_constant_enum
from tests.common import (
MockConfigEntry,
MockEntity,
help_test_all,
import_and_test_deprecated_constant_enum,
setup_test_component_platform,
)
class MockHumidifierEntity(HumidifierEntity):
class MockHumidifierEntity(MockEntity, HumidifierEntity):
"""Mock Humidifier device to use in tests."""
@property
@ -101,3 +113,70 @@ def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) ->
assert "is using deprecated supported features values" not in caplog.text
assert entity.state_attributes[ATTR_MODE] == "mode1"
async def test_humidity_validation(
hass: HomeAssistant,
register_test_integration: MockConfigEntry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test validation for humidity."""
class MockHumidifierEntityHumidity(MockEntity, HumidifierEntity):
"""Mock climate class with mocked aux heater."""
_attr_supported_features = HumidifierEntityFeature.MODES
_attr_available_modes = [MODE_NORMAL, MODE_ECO]
_attr_mode = MODE_NORMAL
_attr_target_humidity = 50
_attr_min_humidity = 50
_attr_max_humidity = 60
def set_humidity(self, humidity: int) -> None:
"""Set new target humidity."""
self._attr_target_humidity = humidity
test_humidifier = MockHumidifierEntityHumidity(
name="Test",
unique_id="unique_humidifier_test",
)
setup_test_component_platform(
hass, HUMIDIFIER_DOMAIN, entities=[test_humidifier], from_config_entry=True
)
await hass.config_entries.async_setup(register_test_integration.entry_id)
await hass.async_block_till_done()
state = hass.states.get("humidifier.test")
assert state.attributes.get(ATTR_HUMIDITY) == 50
with pytest.raises(
ServiceValidationError,
match="Provided humidity 1 is not valid. Accepted range is 50 to 60",
) as exc:
await hass.services.async_call(
HUMIDIFIER_DOMAIN,
SERVICE_SET_HUMIDITY,
{
"entity_id": "humidifier.test",
ATTR_HUMIDITY: "1",
},
blocking=True,
)
assert exc.value.translation_key == "humidity_out_of_range"
assert "Check valid humidity 1 in range 50 - 60" in caplog.text
with pytest.raises(
ServiceValidationError,
match="Provided humidity 70 is not valid. Accepted range is 50 to 60",
) as exc:
await hass.services.async_call(
HUMIDIFIER_DOMAIN,
SERVICE_SET_HUMIDITY,
{
"entity_id": "humidifier.test",
ATTR_HUMIDITY: "70",
},
blocking=True,
)