1 """Support to set a numeric value from a slider or text box."""
3 from __future__
import annotations
5 from contextlib
import suppress
7 from typing
import Self
9 import voluptuous
as vol
18 CONF_UNIT_OF_MEASUREMENT,
30 _LOGGER = logging.getLogger(__name__)
32 DOMAIN =
"input_number"
34 CONF_INITIAL =
"initial"
39 MODE_SLIDER =
"slider"
42 ATTR_INITIAL =
"initial"
48 SERVICE_SET_VALUE =
"set_value"
49 SERVICE_INCREMENT =
"increment"
50 SERVICE_DECREMENT =
"decrement"
54 """Configure validation helper for input number (voluptuous)."""
55 minimum = cfg.get(CONF_MIN)
56 maximum = cfg.get(CONF_MAX)
57 if minimum >= maximum:
59 f
"Maximum ({minimum}) is not greater than minimum ({maximum})"
61 state = cfg.get(CONF_INITIAL)
62 if state
is not None and (state < minimum
or state > maximum):
63 raise vol.Invalid(f
"Initial value {state} not in range {minimum}-{maximum}")
67 STORAGE_FIELDS: VolDictType = {
68 vol.Required(CONF_NAME): vol.All(str, vol.Length(min=1)),
69 vol.Required(CONF_MIN): vol.Coerce(float),
70 vol.Required(CONF_MAX): vol.Coerce(float),
71 vol.Optional(CONF_INITIAL): vol.Coerce(float),
72 vol.Optional(CONF_STEP, default=1): vol.All(vol.Coerce(float), vol.Range(min=1e-9)),
73 vol.Optional(CONF_ICON): cv.icon,
74 vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
75 vol.Optional(CONF_MODE, default=MODE_SLIDER): vol.In([MODE_BOX, MODE_SLIDER]),
78 CONFIG_SCHEMA = vol.Schema(
80 DOMAIN: cv.schema_with_slug_keys(
83 vol.Optional(CONF_NAME): cv.string,
84 vol.Required(CONF_MIN): vol.Coerce(float),
85 vol.Required(CONF_MAX): vol.Coerce(float),
86 vol.Optional(CONF_INITIAL): vol.Coerce(float),
87 vol.Optional(CONF_STEP, default=1): vol.All(
88 vol.Coerce(float), vol.Range(min=1e-9)
90 vol.Optional(CONF_ICON): cv.icon,
91 vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
92 vol.Optional(CONF_MODE, default=MODE_SLIDER): vol.In(
93 [MODE_BOX, MODE_SLIDER]
100 extra=vol.ALLOW_EXTRA,
102 RELOAD_SERVICE_SCHEMA = vol.Schema({})
107 async
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
108 """Set up an input slider."""
109 component = EntityComponent[InputNumber](_LOGGER, DOMAIN, hass)
111 id_manager = collection.IDManager()
113 yaml_collection = collection.YamlCollection(
114 logging.getLogger(f
"{__name__}.yaml_collection"), id_manager
116 collection.sync_entity_lifecycle(
117 hass, DOMAIN, DOMAIN, component, yaml_collection, InputNumber
121 Store(hass, STORAGE_VERSION, STORAGE_KEY),
124 collection.sync_entity_lifecycle(
125 hass, DOMAIN, DOMAIN, component, storage_collection, InputNumber
128 await yaml_collection.async_load(
129 [{CONF_ID: id_, **(conf
or {})}
for id_, conf
in config.get(DOMAIN, {}).items()]
131 await storage_collection.async_load()
133 collection.DictStorageCollectionWebsocket(
134 storage_collection, DOMAIN, DOMAIN, STORAGE_FIELDS, STORAGE_FIELDS
137 async
def reload_service_handler(service_call: ServiceCall) ->
None:
138 """Reload yaml entities."""
139 conf = await component.async_prepare_reload(skip_reset=
True)
142 await yaml_collection.async_load(
143 [{CONF_ID: id_, **conf}
for id_, conf
in conf.get(DOMAIN, {}).items()]
150 reload_service_handler,
151 schema=RELOAD_SERVICE_SCHEMA,
154 component.async_register_entity_service(
156 {vol.Required(ATTR_VALUE): vol.Coerce(float)},
160 component.async_register_entity_service(SERVICE_INCREMENT,
None,
"async_increment")
162 component.async_register_entity_service(SERVICE_DECREMENT,
None,
"async_decrement")
168 """Input storage based collection."""
170 SCHEMA = vol.Schema(vol.All(STORAGE_FIELDS, _cv_input_number))
173 """Validate the config is valid."""
174 return self.
SCHEMASCHEMA(data)
178 """Suggest an ID based on the config."""
179 return info[CONF_NAME]
184 A past bug caused frontend to add initial value to all input numbers.
192 for number
in data[
"items"]:
193 number.pop(CONF_INITIAL,
None)
198 """Return a new updated data object."""
199 update_data = self.
SCHEMASCHEMA(update_data)
200 return {CONF_ID: item[CONF_ID]} | update_data
204 """Representation of a slider."""
206 _unrecorded_attributes = frozenset(
207 {ATTR_EDITABLE, ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_STEP}
210 _attr_should_poll =
False
214 """Initialize an input number."""
216 self.
_current_value_current_value: float |
None = config.get(CONF_INITIAL)
220 """Return entity instance initialized from storage."""
221 input_num = cls(config)
222 input_num.editable =
True
227 """Return entity instance initialized from yaml."""
228 input_num = cls(config)
229 input_num.entity_id = f
"{DOMAIN}.{config[CONF_ID]}"
230 input_num.editable =
False
235 """Return minimum allowed value."""
236 return self.
_config_config[CONF_MIN]
240 """Return maximum allowed value."""
241 return self.
_config_config[CONF_MAX]
245 """Return the name of the input slider."""
250 """Return the icon to be used for this entity."""
255 """Return the state of the component."""
260 """Return entity's increment/decrement step."""
261 return self.
_config_config[CONF_STEP]
265 """Return the unit the value is expressed in."""
266 return self.
_config_config.
get(CONF_UNIT_OF_MEASUREMENT)
270 """Return unique id of the entity."""
271 return self.
_config_config[CONF_ID]
275 """Return the state attributes."""
277 ATTR_INITIAL: self.
_config_config.
get(CONF_INITIAL),
278 ATTR_EDITABLE: self.editable,
281 ATTR_STEP: self.
_step_step,
282 ATTR_MODE: self.
_config_config[CONF_MODE],
286 """Run when entity about to be added to hass."""
291 value: float |
None =
None
293 with suppress(ValueError):
294 value =
float(state.state)
297 if value
is not None and self.
_minimum_minimum <= value <= self.
_maximum_maximum:
304 num_value =
float(value)
308 f
"Invalid value for {self.entity_id}: {value} (range {self._minimum} -"
316 """Increment value."""
320 """Decrement value."""
324 """Handle when the config is updated."""
None async_write_ha_state(self)
State|None async_get_last_state(self)
web.Response get(self, web.Request request, str config_key)
None async_register_admin_service(HomeAssistant hass, str domain, str service, Callable[[ServiceCall], Awaitable[None]|None] service_func, VolSchemaType schema=vol.Schema({}, extra=vol.PREVENT_EXTRA))