Raise ServiceValidationError on number out of range exception (#114589)
This commit is contained in:
parent
31b0b823df
commit
acf2f855fe
9 changed files with 43 additions and 18 deletions
|
@ -15,7 +15,7 @@ import voluptuous as vol
|
|||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_MODE, CONF_UNIT_OF_MEASUREMENT, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, ServiceCall, async_get_hass, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers.config_validation import (
|
||||
PLATFORM_SCHEMA,
|
||||
PLATFORM_SCHEMA_BASE,
|
||||
|
@ -30,6 +30,7 @@ from .const import ( # noqa: F401
|
|||
ATTR_MAX,
|
||||
ATTR_MIN,
|
||||
ATTR_STEP,
|
||||
ATTR_STEP_VALIDATION,
|
||||
ATTR_VALUE,
|
||||
DEFAULT_MAX_VALUE,
|
||||
DEFAULT_MIN_VALUE,
|
||||
|
@ -99,10 +100,17 @@ async def async_set_value(entity: NumberEntity, service_call: ServiceCall) -> No
|
|||
"""Service call wrapper to set a new value."""
|
||||
value = service_call.data["value"]
|
||||
if value < entity.min_value or value > entity.max_value:
|
||||
raise ValueError(
|
||||
f"Value {value} for {entity.entity_id} is outside valid range"
|
||||
f" {entity.min_value} - {entity.max_value}"
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="out_of_range",
|
||||
translation_placeholders={
|
||||
"value": value,
|
||||
"entity_id": entity.entity_id,
|
||||
"min_value": str(entity.min_value),
|
||||
"max_value": str(entity.max_value),
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
native_value = entity.convert_to_native_value(value)
|
||||
# Clamp to the native range
|
||||
|
@ -174,7 +182,7 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
|||
"""Representation of a Number entity."""
|
||||
|
||||
_entity_component_unrecorded_attributes = frozenset(
|
||||
{ATTR_MIN, ATTR_MAX, ATTR_STEP, ATTR_MODE}
|
||||
{ATTR_MIN, ATTR_MAX, ATTR_STEP, ATTR_STEP_VALIDATION, ATTR_MODE}
|
||||
)
|
||||
|
||||
entity_description: NumberEntityDescription
|
||||
|
|
|
@ -54,6 +54,7 @@ ATTR_VALUE = "value"
|
|||
ATTR_MIN = "min"
|
||||
ATTR_MAX = "max"
|
||||
ATTR_STEP = "step"
|
||||
ATTR_STEP_VALIDATION = "step_validation"
|
||||
|
||||
DEFAULT_MIN_VALUE = 0.0
|
||||
DEFAULT_MAX_VALUE = 100.0
|
||||
|
|
|
@ -161,6 +161,11 @@
|
|||
"name": "[%key:component::sensor::entity_component::wind_speed::name%]"
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"out_of_range": {
|
||||
"message": "Value {value} for {entity_id} is outside valid range {min_value} - {max_value}."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"set_value": {
|
||||
"name": "Set",
|
||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.components.number import (
|
|||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, EntityCategory
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from .test_gateway import (
|
||||
|
@ -186,7 +187,7 @@ async def test_number_entities(
|
|||
|
||||
# Service set value beyond the supported range
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
|
|
@ -16,6 +16,7 @@ from homeassistant.components.number import (
|
|||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
ENTITY_VOLUME = "number.volume"
|
||||
|
@ -97,7 +98,7 @@ async def test_set_value_bad_range(hass: HomeAssistant) -> None:
|
|||
state = hass.states.get(ENTITY_VOLUME)
|
||||
assert state.state == "42.0"
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
|
|
@ -21,7 +21,7 @@ from homeassistant.const import (
|
|||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
@ -292,7 +292,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
|
|||
assert state.state == "4"
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -314,7 +314,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
|
|||
bulb.async_set_device_config.assert_called_with(pixels_per_segment=100)
|
||||
bulb.async_set_device_config.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -335,7 +335,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
|
|||
bulb.async_set_device_config.assert_called_with(music_pixels_per_segment=100)
|
||||
bulb.async_set_device_config.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -356,7 +356,7 @@ async def test_addressable_light_pixel_config(hass: HomeAssistant) -> None:
|
|||
bulb.async_set_device_config.assert_called_with(segments=5)
|
||||
bulb.async_set_device_config.reset_mock()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
|
|
@ -6,6 +6,7 @@ from homeassistant.components.knx.const import CONF_RESPOND_TO_READ, KNX_ADDRESS
|
|||
from homeassistant.components.knx.schema import NumberSchema
|
||||
from homeassistant.const import CONF_NAME, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from .conftest import KNXTestKit
|
||||
|
||||
|
@ -37,14 +38,14 @@ async def test_number_set_value(hass: HomeAssistant, knx: KNXTestKit) -> None:
|
|||
assert state.attributes.get("unit_of_measurement") == "%"
|
||||
|
||||
# set value out of range
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
{"entity_id": "number.test", "value": 101.0},
|
||||
blocking=True,
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
"number",
|
||||
"set_value",
|
||||
|
|
|
@ -36,6 +36,7 @@ from homeassistant.const import (
|
|||
UnitOfVolumeFlowRate,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import STORAGE_KEY as RESTORE_STATE_KEY
|
||||
|
@ -359,14 +360,20 @@ async def test_set_value(
|
|||
state = hass.states.get("number.test")
|
||||
assert state.state == "60.0"
|
||||
|
||||
# test ValueError trigger
|
||||
with pytest.raises(ValueError):
|
||||
# test range validation
|
||||
with pytest.raises(ServiceValidationError) as exc:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_VALUE: 110.0, ATTR_ENTITY_ID: "number.test"},
|
||||
blocking=True,
|
||||
)
|
||||
assert exc.value.translation_domain == DOMAIN
|
||||
assert exc.value.translation_key == "out_of_range"
|
||||
assert (
|
||||
str(exc.value)
|
||||
== "Value 110.0 for number.test is outside valid range 0.0 - 100.0"
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("number.test")
|
||||
|
|
|
@ -14,6 +14,7 @@ from homeassistant.components.number import (
|
|||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
@ -86,7 +87,7 @@ async def test_set_number_value_out_of_range(hass: HomeAssistant) -> None:
|
|||
assert state
|
||||
assert state.state == "2"
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -105,7 +106,7 @@ async def test_set_number_value_out_of_range(hass: HomeAssistant) -> None:
|
|||
assert state
|
||||
assert state.state == "2"
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
|
Loading…
Add table
Reference in a new issue