mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 02:24:51 -05:00
Migrate InputNumber entity to inherit NumberEntity (#175772)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2374291a8a
commit
ab91e3202d
@@ -6,7 +6,8 @@ from typing import Any, Self, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
from homeassistant.components.number import NumberEntity
|
||||
from homeassistant.const import ( # noqa: F401
|
||||
ATTR_EDITABLE,
|
||||
ATTR_MODE,
|
||||
CONF_ICON,
|
||||
@@ -149,7 +150,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SET_VALUE,
|
||||
{vol.Required(ATTR_VALUE): vol.Coerce(float)},
|
||||
"async_set_value",
|
||||
"async_set_native_value",
|
||||
)
|
||||
|
||||
component.async_register_entity_service(SERVICE_INCREMENT, None, "async_increment")
|
||||
@@ -199,20 +200,31 @@ class NumberStorageCollection(collection.DictStorageCollection):
|
||||
return {CONF_ID: item[CONF_ID]} | update_data
|
||||
|
||||
|
||||
class InputNumber(collection.CollectionEntity, RestoreEntity):
|
||||
# pylint: disable-next=home-assistant-enforce-class-module
|
||||
class InputNumber(collection.CollectionEntity, NumberEntity, RestoreEntity):
|
||||
"""Representation of a slider."""
|
||||
|
||||
_unrecorded_attributes = frozenset(
|
||||
{ATTR_EDITABLE, ATTR_MAX, ATTR_MIN, ATTR_MODE, ATTR_STEP}
|
||||
)
|
||||
_unrecorded_attributes = frozenset({ATTR_EDITABLE})
|
||||
|
||||
_attr_should_poll = False
|
||||
editable: bool
|
||||
|
||||
def __init__(self, config: ConfigType) -> None:
|
||||
"""Initialize an input number."""
|
||||
self._config = config
|
||||
self._current_value: float | None = config.get(CONF_INITIAL)
|
||||
self._initial_value: float | None = config.get(CONF_INITIAL)
|
||||
self._attr_native_value = self._initial_value
|
||||
self._update_config_attributes(config)
|
||||
|
||||
def _update_config_attributes(self, config: ConfigType) -> None:
|
||||
"""Update attributes based on the config."""
|
||||
self._attr_icon = config.get(CONF_ICON)
|
||||
self._attr_mode = config[CONF_MODE]
|
||||
self._attr_name = config.get(CONF_NAME)
|
||||
self._attr_native_min_value = config[CONF_MIN]
|
||||
self._attr_native_max_value = config[CONF_MAX]
|
||||
self._attr_native_step = config[CONF_STEP]
|
||||
self._attr_unique_id = config[CONF_ID]
|
||||
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
@@ -231,69 +243,20 @@ class InputNumber(collection.CollectionEntity, RestoreEntity):
|
||||
input_num.editable = False
|
||||
return input_num
|
||||
|
||||
@property
|
||||
def _minimum(self) -> float:
|
||||
"""Return minimum allowed value."""
|
||||
return self._config[CONF_MIN]
|
||||
|
||||
@property
|
||||
def _maximum(self) -> float:
|
||||
"""Return maximum allowed value."""
|
||||
return self._config[CONF_MAX]
|
||||
|
||||
@property
|
||||
@override
|
||||
def name(self):
|
||||
"""Return the name of the input slider."""
|
||||
return self._config.get(CONF_NAME)
|
||||
|
||||
@property
|
||||
@override
|
||||
def icon(self) -> str | None:
|
||||
"""Return the icon to be used for this entity."""
|
||||
return self._config.get(CONF_ICON)
|
||||
|
||||
@property
|
||||
@override
|
||||
def state(self):
|
||||
"""Return the state of the component."""
|
||||
return self._current_value
|
||||
|
||||
@property
|
||||
def _step(self) -> int:
|
||||
"""Return entity's increment/decrement step."""
|
||||
return self._config[CONF_STEP]
|
||||
|
||||
@property
|
||||
@override
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit the value is expressed in."""
|
||||
return self._config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
|
||||
@property
|
||||
@override
|
||||
def unique_id(self) -> str | None:
|
||||
"""Return unique id of the entity."""
|
||||
return self._config[CONF_ID]
|
||||
|
||||
@property
|
||||
@override
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes."""
|
||||
return {
|
||||
ATTR_INITIAL: self._config.get(CONF_INITIAL),
|
||||
ATTR_INITIAL: self._initial_value,
|
||||
ATTR_EDITABLE: self.editable,
|
||||
ATTR_MIN: self._minimum,
|
||||
ATTR_MAX: self._maximum,
|
||||
ATTR_STEP: self._step,
|
||||
ATTR_MODE: self._config[CONF_MODE],
|
||||
}
|
||||
|
||||
@override
|
||||
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:
|
||||
if self._attr_native_value is not None:
|
||||
return
|
||||
|
||||
value: float | None = None
|
||||
@@ -302,39 +265,47 @@ class InputNumber(collection.CollectionEntity, RestoreEntity):
|
||||
value = 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
|
||||
if (
|
||||
value is not None
|
||||
and self.native_min_value <= value <= self.native_max_value
|
||||
):
|
||||
self._attr_native_value = value
|
||||
else:
|
||||
self._current_value = self._minimum
|
||||
self._attr_native_value = self.native_min_value
|
||||
|
||||
async def async_set_value(self, value):
|
||||
@override
|
||||
async def async_set_native_value(self, value):
|
||||
"""Set new value."""
|
||||
num_value = float(value)
|
||||
|
||||
if num_value < self._minimum or num_value > self._maximum:
|
||||
if num_value < self.native_min_value or num_value > self.native_max_value:
|
||||
raise vol.Invalid(
|
||||
f"Invalid value for {self.entity_id}: {value} (range {self._minimum} -"
|
||||
f" {self._maximum})"
|
||||
f"Invalid value for {self.entity_id}: {value} (range "
|
||||
f"{self.native_min_value} - {self.native_max_value})"
|
||||
)
|
||||
|
||||
self._current_value = num_value
|
||||
self._attr_native_value = num_value
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_increment(self):
|
||||
"""Increment value."""
|
||||
await self.async_set_value(min(self._current_value + self._step, self._maximum))
|
||||
await self.async_set_native_value(
|
||||
min(self._attr_native_value + self.native_step, self.native_max_value)
|
||||
)
|
||||
|
||||
async def async_decrement(self):
|
||||
"""Decrement value."""
|
||||
await self.async_set_value(max(self._current_value - self._step, self._minimum))
|
||||
await self.async_set_native_value(
|
||||
max(self._attr_native_value - self.native_step, self.native_min_value)
|
||||
)
|
||||
|
||||
@override
|
||||
async def async_update_config(self, config: ConfigType) -> None:
|
||||
"""Handle when the config is updated."""
|
||||
self._config = config
|
||||
self._update_config_attributes(config)
|
||||
# just in case min/max values changed
|
||||
if self._current_value is None:
|
||||
if self._attr_native_value is None:
|
||||
return
|
||||
self._current_value = min(self._current_value, self._maximum)
|
||||
self._current_value = max(self._current_value, self._minimum)
|
||||
self._attr_native_value = min(self._attr_native_value, self.native_max_value)
|
||||
self._attr_native_value = max(self._attr_native_value, self.native_min_value)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -19,6 +19,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_NAME,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from homeassistant.core import Context, CoreState, HomeAssistant, State
|
||||
from homeassistant.exceptions import Unauthorized
|
||||
@@ -237,6 +238,28 @@ async def test_mode(hass: HomeAssistant) -> None:
|
||||
assert state.attributes["mode"] == "slider"
|
||||
|
||||
|
||||
async def test_unit_of_measurement(hass: HomeAssistant) -> None:
|
||||
"""Test unit of measurement is exposed in the state attributes."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
"with_unit": {"min": 0, "max": 100, "unit_of_measurement": "°C"},
|
||||
"without_unit": {"min": 0, "max": 100},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
state = hass.states.get("input_number.with_unit")
|
||||
assert state
|
||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "°C"
|
||||
|
||||
state = hass.states.get("input_number.without_unit")
|
||||
assert state
|
||||
assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes
|
||||
|
||||
|
||||
async def test_restore_state(hass: HomeAssistant) -> None:
|
||||
"""Ensure states are restored on startup."""
|
||||
mock_restore_cache(
|
||||
|
||||
Reference in New Issue
Block a user