diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index a20acb157325..4acf92e9e4df 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -8,7 +8,6 @@ import logging from math import ceil, floor from typing import TYPE_CHECKING, Any, Self, final, override -import probatio from propcache.api import cached_property from homeassistant.config_entries import ConfigEntry @@ -17,20 +16,13 @@ from homeassistant.const import ( # noqa: F401 CONF_UNIT_OF_MEASUREMENT, UnitOfTemperature, ) -from homeassistant.core import ( - HomeAssistant, - ServiceCall, - async_get_hass_or_none, - callback, -) -from homeassistant.exceptions import ServiceValidationError +from homeassistant.core import HomeAssistant, async_get_hass_or_none, callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import ExtraStoredData, RestoreEntity from homeassistant.helpers.typing import ConfigType from homeassistant.loader import async_suggest_report_issue -from homeassistant.util.hass_dict import HassKey from .const import ( # noqa: F401 AMBIGUOUS_UNITS, @@ -38,6 +30,7 @@ from .const import ( # noqa: F401 ATTR_MIN, ATTR_STEP, ATTR_VALUE, + DATA_COMPONENT, DEFAULT_MAX_VALUE, DEFAULT_MIN_VALUE, DEFAULT_STEP, @@ -50,11 +43,11 @@ from .const import ( # noqa: F401 NumberEntityCapabilityAttribute, NumberMode, ) +from .services import async_setup_services from .websocket_api import async_setup as async_setup_ws_api _LOGGER = logging.getLogger(__name__) -DATA_COMPONENT: HassKey[EntityComponent[NumberEntity]] = HassKey(DOMAIN) ENTITY_ID_FORMAT = DOMAIN + ".{}" PLATFORM_SCHEMA = cv.PLATFORM_SCHEMA PLATFORM_SCHEMA_BASE = cv.PLATFORM_SCHEMA_BASE @@ -94,41 +87,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async_setup_ws_api(hass) await component.async_setup(config) - component.async_register_entity_service( - SERVICE_SET_VALUE, - {probatio.Required(ATTR_VALUE): probatio.Coerce(float)}, - async_set_value, - ) + async_setup_services(hass) return True -async def async_set_value(entity: NumberEntity, service_call: ServiceCall) -> None: - """Service call wrapper to set a new value.""" - value = service_call.data["value"] - if value < entity.min_value or 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 - native_value = min( - max(native_value, entity.native_min_value), entity.native_max_value - ) - await entity.async_set_native_value(native_value) - except NotImplementedError: - await entity.async_set_value(value) - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up a config entry.""" return await hass.data[DATA_COMPONENT].async_setup_entry(entry) diff --git a/homeassistant/components/number/const.py b/homeassistant/components/number/const.py index 5344b46ecaf1..b25135ae8f75 100644 --- a/homeassistant/components/number/const.py +++ b/homeassistant/components/number/const.py @@ -1,7 +1,7 @@ """Provides the constants needed for the component.""" from enum import StrEnum -from typing import Final +from typing import TYPE_CHECKING, Final import probatio @@ -40,6 +40,7 @@ from homeassistant.const import ( UnitOfVolumeFlowRate, UnitOfVolumetricFlux, ) +from homeassistant.util.hass_dict import HassKey from homeassistant.util.unit_conversion import ( ApparentPowerConverter, AreaConverter, @@ -77,6 +78,13 @@ from homeassistant.util.unit_conversion import ( DOMAIN: Final = "number" +if TYPE_CHECKING: + from homeassistant.helpers.entity_component import EntityComponent + + from . import NumberEntity + +DATA_COMPONENT: HassKey[EntityComponent[NumberEntity]] = HassKey(DOMAIN) + ATTR_VALUE = "value" ATTR_MIN = "min" ATTR_MAX = "max" diff --git a/homeassistant/components/number/services.py b/homeassistant/components/number/services.py new file mode 100644 index 000000000000..cb395d95524e --- /dev/null +++ b/homeassistant/components/number/services.py @@ -0,0 +1,49 @@ +"""Services for the Number integration.""" + +from typing import TYPE_CHECKING + +import probatio + +from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.exceptions import ServiceValidationError + +from .const import ATTR_VALUE, DATA_COMPONENT, DOMAIN, SERVICE_SET_VALUE + +if TYPE_CHECKING: + from . import NumberEntity + + +async def _async_set_value(entity: NumberEntity, service_call: ServiceCall) -> None: + """Service call wrapper to set a new value.""" + value = service_call.data["value"] + if value < entity.min_value or 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 + native_value = min( + max(native_value, entity.native_min_value), entity.native_max_value + ) + await entity.async_set_native_value(native_value) + except NotImplementedError: + await entity.async_set_value(value) + + +@callback +def async_setup_services(hass: HomeAssistant) -> None: + """Register the number services.""" + hass.data[DATA_COMPONENT].async_register_entity_service( + SERVICE_SET_VALUE, + {probatio.Required(ATTR_VALUE): probatio.Coerce(float)}, + _async_set_value, + )