Service to change min/max for input_number?

Is it possible to adjust the min/max values of an input_number dynamically? Based on the input_number documentation, I don’t think it is but maybe someone knows of a sneaky way to update those values???

I am working on a configuration panel for OpenEVSE electric vehicle charger and depending on wether it is “L1” or “L2” mode the min/max charge current possible is 6-16 Amps or 10-80 Amps, respectively. I would rather not have to create an L1_charge_current and an L2_charge_current slider and hide/reveal which one depending on the service level.

I can’t think of a way, but perhaps you could create scripts and treat them like buttons, for example, a ‘min’ script would set it to 6 or 10, a ‘max’ script would set it to 16 or 80.

Other solution would be to have your slider from 1-100 and then do some math to convert to a 6-16 scale or 10-80 scale.

1 Like

That’s a good solution.

Thanks for that, scaling the input value is a good idea.

I’m actually trying to extend the built-in as a custom component right now. It seems like it should be easy – famous last words.

You are going to have to be careful when changing ranges that the current set point is not outside the new range.

e.g. currently set to 80 and you change to a 6-16 range.

Quick and dirty custom component that allows min/max adjustments. Fully backwards compatible with the existing input_number. Seems to work great with Lovelace, I didn’t test it on State UI. It clamps the value to min/max as appropriate and refuses to set min>=max or max<=min.

To change min/max use:




  "entity_id": "input_number.myinput",
  "value": 1

Place this into config/custom_components

Component to offer a way to set a numeric value from a slider or text box.

For more details about this component, please refer to the documentation
import logging

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.restore_state import RestoreEntity

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'input_number'

CONF_INITIAL = 'initial'
CONF_MIN = 'min'
CONF_MAX = 'max'
CONF_STEP = 'step'

MODE_SLIDER = 'slider'
MODE_BOX = 'box'

ATTR_INITIAL = 'initial'
ATTR_VALUE = 'value'
ATTR_MIN = 'min'
ATTR_MAX = 'max'
ATTR_STEP = 'step'
ATTR_MODE = 'mode'

SERVICE_SET_VALUE = 'set_value'
SERVICE_SET_MIN = 'set_min'
SERVICE_SET_MAX = 'set_max'

    vol.Optional(ATTR_ENTITY_ID): cv.entity_ids

    vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
    vol.Required(ATTR_VALUE): vol.Coerce(float),

def _cv_input_number(cfg):
    """Configure validation helper for input number (voluptuous)."""
    minimum = cfg.get(CONF_MIN)
    maximum = cfg.get(CONF_MAX)
    if minimum >= maximum:
        raise vol.Invalid('Maximum ({}) is not greater than minimum ({})'
                          .format(minimum, maximum))
    state = cfg.get(CONF_INITIAL)
    if state is not None and (state < minimum or state > maximum):
        raise vol.Invalid('Initial value {} not in range {}-{}'
                          .format(state, minimum, maximum))
    return cfg

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: cv.schema_with_slug_keys(
            vol.Optional(CONF_NAME): cv.string,
            vol.Required(CONF_MIN): vol.Coerce(float),
            vol.Required(CONF_MAX): vol.Coerce(float),
            vol.Optional(CONF_INITIAL): vol.Coerce(float),
            vol.Optional(CONF_STEP, default=1):
                vol.All(vol.Coerce(float), vol.Range(min=1e-3)),
            vol.Optional(CONF_ICON): cv.icon,
            vol.Optional(ATTR_UNIT_OF_MEASUREMENT): cv.string,
            vol.Optional(CONF_MODE, default=MODE_SLIDER):
                vol.In([MODE_BOX, MODE_SLIDER]),
        }, _cv_input_number)
}, required=True, extra=vol.ALLOW_EXTRA)

async def async_setup(hass, config):
    """Set up an input slider."""
    component = EntityComponent(_LOGGER, DOMAIN, hass)

    entities = []

    for object_id, cfg in config[DOMAIN].items():
        name = cfg.get(CONF_NAME)
        minimum = cfg.get(CONF_MIN)
        maximum = cfg.get(CONF_MAX)
        initial = cfg.get(CONF_INITIAL)
        step = cfg.get(CONF_STEP)
        icon = cfg.get(CONF_ICON)
        unit = cfg.get(ATTR_UNIT_OF_MEASUREMENT)
        mode = cfg.get(CONF_MODE)

            object_id, name, initial, minimum, maximum, step, icon, unit,

    if not entities:
        return False






    await component.async_add_entities(entities)
    return True

class InputNumber(RestoreEntity):
    """Representation of a slider."""

    def __init__(self, object_id, name, initial, minimum, maximum, step, icon,
                 unit, mode):
        """Initialize an input number."""
        self.entity_id = ENTITY_ID_FORMAT.format(object_id)
        self._name = name
        self._current_value = initial
        self._initial = initial
        self._minimum = minimum
        self._maximum = maximum
        self._step = step
        self._icon = icon
        self._unit = unit
        self._mode = mode

    def should_poll(self):
        """If entity should be polled."""
        return False

    def name(self):
        """Return the name of the input slider."""
        return self._name

    def icon(self):
        """Return the icon to be used for this entity."""
        return self._icon

    def state(self):
        """Return the state of the component."""
        return self._current_value

    def unit_of_measurement(self):
        """Return the unit the value is expressed in."""
        return self._unit

    def state_attributes(self):
        """Return the state attributes."""
        return {
            ATTR_INITIAL: self._initial,
            ATTR_MIN: self._minimum,
            ATTR_MAX: self._maximum,
            ATTR_STEP: self._step,
            ATTR_MODE: self._mode,

    async def async_added_to_hass(self):
        """Run when entity about to be added to hass."""
        await super().async_added_to_hass()
        if self._current_value is not None:

        state = await self.async_get_last_state()
        value = state and float(state.state)

        # Check against None because value can be 0
        if value is not None and self._minimum <= value <= self._maximum:
            self._current_value = value
            self._current_value = self._minimum

    async def async_set_value(self, value):
        """Set new value."""
        num_value = float(value)
        if num_value < self._minimum or num_value > self._maximum:
            _LOGGER.warning("Invalid value: %s (range %s - %s)",
                            num_value, self._minimum, self._maximum)
        self._current_value = num_value
        await self.async_update_ha_state()

    async def async_set_min(self, value):
        """Set new min value."""
        minimum = float(value)
        if minimum >= self._maximum:
            _LOGGER.warning("Invalid minimum: %s (must be lower than maximum %s)",
                            minimum, self._maximum)
        # Adjust value to minimim if it is less than the new minimum
        if minimum > self._current_value:
            self._current_value = minimum
        self._minimum = minimum
        await self.async_update_ha_state()

    async def async_set_max(self, value):
        """Set new max value."""
        maximum = float(value)
        if self._minimum >= maximum:
            _LOGGER.warning("Invalid maximum: %s (must be higher than minimum %s)",
                            maximum, self._minimum)
        if maximum < self._current_value:
            self._current_value = maximum
        self._maximum = maximum
        await self.async_update_ha_state()

    async def async_increment(self):
        """Increment value."""
        new_value = self._current_value + self._step
        if new_value > self._maximum:
            _LOGGER.warning("Invalid value: %s (range %s - %s)",
                            new_value, self._minimum, self._maximum)
        self._current_value = new_value
        await self.async_update_ha_state()

    async def async_decrement(self):
        """Decrement value."""
        new_value = self._current_value - self._step
        if new_value < self._minimum:
            _LOGGER.warning("Invalid value: %s (range %s - %s)",
                            new_value, self._minimum, self._maximum)
        self._current_value = new_value
        await self.async_update_ha_state()
1 Like

Updates here as I make improvements:

Not sure if this sort of change would be accepted as a PR, but I assume I’ll need to update documentation and tests before making a Pull Request.

Did you get anywhere with getting this merged? I have need for it as well, though I’ll just use your custom_component for now :slight_smile:

Oops, I kinda forgot about it. Never submitted it. The last few months it has been working OK, I haven’t needed to make any edits.

I will take a look at how to submit it as a PR.

No problem, it’s working great for me too (thanks!), just a bit worried in case they make a change down the line that breaks it I suppose. Having it officially supported would help with that I guess.

@a3a Is it just me or did this break in 0.92?

Yes and no… 0.92 doesn’t really break the custom-component per se, since there is not a code change needed, but it does change where the code belongs and what it is named in order for it to be loaded. See

I will have to rename and relocate the custom component.

As an aside, I am working on getting my build environment set up so I can build tests and then submit a pull-request to (hopefully) integrate this custom component into core.

Hmm, do they just mean /custom_components/input_number/ (and the init file)?

The new location is custom_components/input_number/ I’ve updated my github repo with the required changes: input_number. You need to copy all of the files in the folder:, manifest.json, and services.yaml. This approach does make it easier to specify documentation and other metadata, so it’s a good change… just takes a bit of time to update everything to work in the new world.

Gotcha, thanks :slight_smile: