Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

200 lines
6.2 KiB
Python
Raw Permalink Normal View History

"""Support for numbers which integrates with other components."""
from typing import TYPE_CHECKING, Any
import voluptuous as vol
2022-09-12 18:14:49 +02:00
from homeassistant.components.number import (
ATTR_VALUE,
DEFAULT_MAX_VALUE,
DEFAULT_MIN_VALUE,
DEFAULT_STEP,
2026-04-28 08:12:18 -04:00
DEVICE_CLASSES_SCHEMA,
DOMAIN as NUMBER_DOMAIN,
ENTITY_ID_FORMAT,
2022-09-12 18:14:49 +02:00
NumberEntity,
)
from homeassistant.config_entries import ConfigEntry
2026-04-28 08:12:18 -04:00
from homeassistant.const import (
CONF_DEVICE_CLASS,
CONF_NAME,
CONF_STATE,
CONF_UNIT_OF_MEASUREMENT,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
)
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator, validators as template_validators
from .const import CONF_MAX, CONF_MIN, CONF_STEP, DOMAIN
from .entity import AbstractTemplateEntity
from .helpers import (
async_setup_template_entry,
async_setup_template_platform,
async_setup_template_preview,
)
from .schemas import (
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA,
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA,
make_template_entity_common_modern_schema,
)
from .template_entity import TemplateEntity
from .trigger_entity import TriggerEntity
CONF_SET_VALUE = "set_value"
DEFAULT_NAME = "Template Number"
DEFAULT_OPTIMISTIC = False
SCRIPT_FIELDS = (CONF_SET_VALUE,)
NUMBER_COMMON_SCHEMA = vol.Schema(
{
2026-04-28 08:12:18 -04:00
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_MAX, default=DEFAULT_MAX_VALUE): cv.template,
vol.Optional(CONF_MIN, default=DEFAULT_MIN_VALUE): cv.template,
vol.Required(CONF_SET_VALUE): cv.SCRIPT_SCHEMA,
vol.Optional(CONF_STATE): cv.template,
vol.Optional(CONF_STEP, default=DEFAULT_STEP): cv.template,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
}
)
NUMBER_YAML_SCHEMA = NUMBER_COMMON_SCHEMA.extend(
TEMPLATE_ENTITY_OPTIMISTIC_SCHEMA
).extend(make_template_entity_common_modern_schema(NUMBER_DOMAIN, DEFAULT_NAME).schema)
NUMBER_CONFIG_ENTRY_SCHEMA = NUMBER_COMMON_SCHEMA.extend(
TEMPLATE_ENTITY_COMMON_CONFIG_ENTRY_SCHEMA.schema
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
"""Set up the template number."""
await async_setup_template_platform(
hass,
NUMBER_DOMAIN,
config,
StateNumberEntity,
TriggerNumberEntity,
async_add_entities,
discovery_info,
script_options=SCRIPT_FIELDS,
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Initialize config entry."""
await async_setup_template_entry(
hass,
config_entry,
async_add_entities,
StateNumberEntity,
NUMBER_CONFIG_ENTRY_SCHEMA,
script_options=SCRIPT_FIELDS,
)
@callback
def async_create_preview_number(
hass: HomeAssistant, name: str, config: dict[str, Any]
) -> StateNumberEntity:
"""Create a preview number."""
return async_setup_template_preview(
hass, name, config, StateNumberEntity, NUMBER_CONFIG_ENTRY_SCHEMA
)
class AbstractTemplateNumber(AbstractTemplateEntity, NumberEntity):
"""Representation of a template number features."""
_entity_id_format = ENTITY_ID_FORMAT
_optimistic_entity = True
_state_option = CONF_STATE
# The super init is not called because TemplateEntity and TriggerEntity will call AbstractTemplateEntity.__init__.
# This ensures that the __init__ on AbstractTemplateEntity is not called twice.
def __init__(self, name: str, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
"""Initialize the features."""
2026-04-28 08:12:18 -04:00
self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)
self._attr_native_step = DEFAULT_STEP
self._attr_native_min_value = DEFAULT_MIN_VALUE
self._attr_native_max_value = DEFAULT_MAX_VALUE
self.setup_state_template(
"_attr_native_value",
template_validators.number(self, CONF_STATE),
)
for option, attribute in (
(CONF_STEP, "_attr_native_step"),
(CONF_MIN, "_attr_native_min_value"),
(CONF_MAX, "_attr_native_max_value"),
):
self.setup_template(
option, attribute, template_validators.number(self, option)
)
self.add_script(CONF_SET_VALUE, config[CONF_SET_VALUE], name, DOMAIN)
async def async_set_native_value(self, value: float) -> None:
"""Set value of the number."""
if self._attr_assumed_state:
self._attr_native_value = value
self.async_write_ha_state()
if set_value := self._action_scripts.get(CONF_SET_VALUE):
await self.async_run_script(
set_value,
run_variables={ATTR_VALUE: value},
context=self._context,
)
class StateNumberEntity(TemplateEntity, AbstractTemplateNumber):
"""Representation of a template number."""
_attr_should_poll = False
def __init__(
self,
hass: HomeAssistant,
config: ConfigType,
unique_id: str | None,
) -> None:
"""Initialize the number."""
TemplateEntity.__init__(self, hass, config, unique_id)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None
AbstractTemplateNumber.__init__(self, name, config)
class TriggerNumberEntity(TriggerEntity, AbstractTemplateNumber):
"""Number entity based on trigger data."""
domain = NUMBER_DOMAIN
def __init__(
self,
hass: HomeAssistant,
coordinator: TriggerUpdateCoordinator,
config: dict,
) -> None:
"""Initialize the entity."""
TriggerEntity.__init__(self, hass, coordinator, config)
name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
AbstractTemplateNumber.__init__(self, name, config)