1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Update template vacuum platform to new template entity framework (#162564)

This commit is contained in:
Petro31
2026-02-08 16:27:21 -05:00
committed by GitHub
parent 5a2299e8b6
commit 3c66a1b35d
2 changed files with 27 additions and 164 deletions
+25 -162
View File
@@ -2,7 +2,6 @@
from __future__ import annotations
from collections.abc import Generator, Sequence
import logging
from typing import TYPE_CHECKING, Any
@@ -30,15 +29,9 @@ from homeassistant.const import (
CONF_STATE,
CONF_UNIQUE_ID,
CONF_VALUE_TEMPLATE,
STATE_UNKNOWN,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import (
config_validation as cv,
issue_registry as ir,
template,
)
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.helpers.entity_platform import (
AddConfigEntryEntitiesCallback,
AddEntitiesCallback,
@@ -46,8 +39,8 @@ from homeassistant.helpers.entity_platform import (
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from . import TriggerUpdateCoordinator, validators as template_validators
from .const import DOMAIN
from .coordinator import TriggerUpdateCoordinator
from .entity import AbstractTemplateEntity
from .helpers import (
async_setup_template_entry,
@@ -76,14 +69,6 @@ CONF_FAN_SPEED_TEMPLATE = "fan_speed_template"
DEFAULT_NAME = "Template Vacuum"
ENTITY_ID_FORMAT = VACUUM_DOMAIN + ".{}"
_VALID_STATES = [
VacuumActivity.CLEANING,
VacuumActivity.DOCKED,
VacuumActivity.PAUSED,
VacuumActivity.IDLE,
VacuumActivity.RETURNING,
VacuumActivity.ERROR,
]
LEGACY_FIELDS = {
CONF_BATTERY_LEVEL_TEMPLATE: CONF_BATTERY_LEVEL,
@@ -225,27 +210,36 @@ class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
# 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, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
def __init__(self, name: str, config: dict[str, Any]) -> None: # pylint: disable=super-init-not-called
"""Initialize the features."""
self._battery_level_template = config.get(CONF_BATTERY_LEVEL)
self._fan_speed_template = config.get(CONF_FAN_SPEED)
self._battery_level = None
self._attr_fan_speed = None
# List of valid fan speeds
self._attr_fan_speed_list = config[CONF_FAN_SPEED_LIST]
self.setup_state_template(
CONF_STATE,
"_attr_activity",
template_validators.strenum(self, CONF_STATE, VacuumActivity),
)
self.setup_template(
CONF_FAN_SPEED,
"_attr_fan_speed",
template_validators.item_in_list(
self, CONF_FAN_SPEED, self._attr_fan_speed_list
),
)
self.setup_template(
CONF_BATTERY_LEVEL,
"_attr_battery_level",
template_validators.number(self, CONF_BATTERY_LEVEL, 0.0, 100.0),
)
self._attr_supported_features = (
VacuumEntityFeature.START | VacuumEntityFeature.STATE
)
if self._battery_level_template:
if CONF_BATTERY_LEVEL in self._templates:
self._attr_supported_features |= VacuumEntityFeature.BATTERY
def _iterate_scripts(
self, config: dict[str, Any]
) -> Generator[tuple[str, Sequence[dict[str, Any]], VacuumEntityFeature | int]]:
for action_id, supported_feature in (
(SERVICE_START, 0),
(SERVICE_PAUSE, VacuumEntityFeature.PAUSE),
@@ -256,22 +250,8 @@ class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
(SERVICE_SET_FAN_SPEED, VacuumEntityFeature.FAN_SPEED),
):
if (action_config := config.get(action_id)) is not None:
yield (action_id, action_config, supported_feature)
def _handle_state(self, result: Any) -> None:
# Validate state
if result in _VALID_STATES:
self._attr_activity = result
elif result == STATE_UNKNOWN:
self._attr_activity = None
else:
_LOGGER.error(
"Received invalid vacuum state: %s for entity %s. Expected: %s",
result,
self.entity_id,
", ".join(_VALID_STATES),
)
self._attr_activity = None
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
async def async_start(self) -> None:
"""Start or resume the cleaning task."""
@@ -335,44 +315,6 @@ class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity):
script, run_variables={ATTR_FAN_SPEED: fan_speed}, context=self._context
)
@callback
def _update_battery_level(self, battery_level):
try:
battery_level_int = int(battery_level)
if not 0 <= battery_level_int <= 100:
raise ValueError # noqa: TRY301
except ValueError:
_LOGGER.error(
"Received invalid battery level: %s for entity %s. Expected: 0-100",
battery_level,
self.entity_id,
)
self._attr_battery_level = None
return
self._attr_battery_level = battery_level_int
@callback
def _update_fan_speed(self, fan_speed):
if isinstance(fan_speed, TemplateError):
# This is legacy behavior
self._attr_fan_speed = None
self._attr_activity = None
return
if fan_speed in self._attr_fan_speed_list:
self._attr_fan_speed = fan_speed
elif fan_speed == STATE_UNKNOWN:
self._attr_fan_speed = None
else:
_LOGGER.error(
"Received invalid fan speed: %s for entity %s. Expected: %s",
fan_speed,
self.entity_id,
self._attr_fan_speed_list,
)
self._attr_fan_speed = None
class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
"""A template vacuum component."""
@@ -387,16 +329,10 @@ class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
) -> None:
"""Initialize the vacuum."""
TemplateEntity.__init__(self, hass, config, unique_id)
AbstractTemplateVacuum.__init__(self, config)
name = self._attr_name
if TYPE_CHECKING:
assert name is not None
for action_id, action_config, supported_feature in self._iterate_scripts(
config
):
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
AbstractTemplateVacuum.__init__(self, name, config)
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
@@ -408,42 +344,6 @@ class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum):
self.entity_id,
)
@callback
def _async_setup_templates(self) -> None:
"""Set up templates."""
if self._template is not None:
self.add_template_attribute(
"_attr_activity", self._template, None, self._update_state
)
if self._fan_speed_template is not None:
self.add_template_attribute(
"_fan_speed",
self._fan_speed_template,
None,
self._update_fan_speed,
)
if self._battery_level_template is not None:
self.add_template_attribute(
"_battery_level",
self._battery_level_template,
None,
self._update_battery_level,
none_on_template_error=True,
)
super()._async_setup_templates()
@callback
def _update_state(self, result):
super()._update_state(result)
if isinstance(result, TemplateError):
# This is legacy behavior
self._attr_activity = None
if not self._availability_template:
self._attr_available = True
return
self._handle_state(result)
class TriggerVacuumEntity(TriggerEntity, AbstractTemplateVacuum):
"""Vacuum entity based on trigger data."""
@@ -458,20 +358,8 @@ class TriggerVacuumEntity(TriggerEntity, AbstractTemplateVacuum):
) -> None:
"""Initialize the entity."""
TriggerEntity.__init__(self, hass, coordinator, config)
AbstractTemplateVacuum.__init__(self, config)
self._attr_name = name = self._rendered.get(CONF_NAME, DEFAULT_NAME)
for action_id, action_config, supported_feature in self._iterate_scripts(
config
):
self.add_script(action_id, action_config, name, DOMAIN)
self._attr_supported_features |= supported_feature
for key in (CONF_STATE, CONF_FAN_SPEED, CONF_BATTERY_LEVEL):
if isinstance(config.get(key), template.Template):
self._to_render_simple.append(key)
self._parse_result.add(key)
AbstractTemplateVacuum.__init__(self, name, config)
async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass."""
@@ -482,28 +370,3 @@ class TriggerVacuumEntity(TriggerEntity, AbstractTemplateVacuum):
self._attr_name or DEFAULT_NAME,
self.entity_id,
)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle update of the data."""
self._process_data()
if not self.available:
return
write_ha_state = False
for key, updater in (
(CONF_STATE, self._handle_state),
(CONF_FAN_SPEED, self._update_fan_speed),
(CONF_BATTERY_LEVEL, self._update_battery_level),
):
if (rendered := self._rendered.get(key)) is not None:
updater(rendered)
write_ha_state = True
if len(self._rendered) > 0:
# In case any non optimistic template
write_ha_state = True
if write_ha_state:
self.async_write_ha_state()
+2 -2
View File
@@ -312,7 +312,7 @@ async def setup_attributes_state_vacuum(
"fan_speed_template": "{{ this_function_does_not_exist() }}",
"start": {"service": "script.vacuum_start"},
},
STATE_UNKNOWN,
STATE_UNAVAILABLE,
None,
),
(
@@ -323,7 +323,7 @@ async def setup_attributes_state_vacuum(
"fan_speed": "{{ this_function_does_not_exist() }}",
"start": {"service": "script.vacuum_start"},
},
STATE_UNKNOWN,
STATE_UNAVAILABLE,
None,
),
(