diff --git a/homeassistant/components/template/strings.json b/homeassistant/components/template/strings.json index be5fb1866ea..96c8435c25c 100644 --- a/homeassistant/components/template/strings.json +++ b/homeassistant/components/template/strings.json @@ -440,6 +440,12 @@ } } }, + "issues": { + "deprecated_battery_level": { + "title": "Deprecated battery level option in {entity_name}", + "description": "The template vacuum options `battery_level` and `battery_level_template` are being removed in 2026.8.\n\nPlease remove the `battery_level` or `battery_level_template` option from the YAML configuration for {entity_id} ({entity_name})." + } + }, "options": { "step": { "alarm_control_panel": { diff --git a/homeassistant/components/template/vacuum.py b/homeassistant/components/template/vacuum.py index 1abfdbd00da..242a534187a 100644 --- a/homeassistant/components/template/vacuum.py +++ b/homeassistant/components/template/vacuum.py @@ -34,11 +34,16 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import TemplateError -from homeassistant.helpers import config_validation as cv, template +from homeassistant.helpers import ( + config_validation as cv, + issue_registry as ir, + template, +) from homeassistant.helpers.entity_platform import ( AddConfigEntryEntitiesCallback, AddEntitiesCallback, ) +from homeassistant.helpers.issue_registry import IssueSeverity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import DOMAIN @@ -188,6 +193,26 @@ def async_create_preview_vacuum( ) +def create_issue( + hass: HomeAssistant, supported_features: int, name: str, entity_id: str +) -> None: + """Create the battery_level issue.""" + if supported_features & VacuumEntityFeature.BATTERY: + key = "deprecated_battery_level" + ir.async_create_issue( + hass, + DOMAIN, + f"{key}_{entity_id}", + is_fixable=False, + severity=IssueSeverity.WARNING, + translation_key=key, + translation_placeholders={ + "entity_name": name, + "entity_id": entity_id, + }, + ) + + class AbstractTemplateVacuum(AbstractTemplateEntity, StateVacuumEntity): """Representation of a template vacuum features.""" @@ -369,6 +394,16 @@ class TemplateStateVacuumEntity(TemplateEntity, AbstractTemplateVacuum): self.add_script(action_id, action_config, name, DOMAIN) self._attr_supported_features |= supported_feature + async def async_added_to_hass(self) -> None: + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + create_issue( + self.hass, + self._attr_supported_features, + self._attr_name or DEFAULT_NAME, + self.entity_id, + ) + @callback def _async_setup_templates(self) -> None: """Set up templates.""" @@ -434,6 +469,16 @@ class TriggerVacuumEntity(TriggerEntity, AbstractTemplateVacuum): self._to_render_simple.append(key) self._parse_result.add(key) + async def async_added_to_hass(self) -> None: + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + create_issue( + self.hass, + self._attr_supported_features, + self._attr_name or DEFAULT_NAME, + self.entity_id, + ) + @callback def _handle_coordinator_update(self) -> None: """Handle update of the data.""" diff --git a/tests/components/template/test_vacuum.py b/tests/components/template/test_vacuum.py index 6c7222645b6..d0e6488e46e 100644 --- a/tests/components/template/test_vacuum.py +++ b/tests/components/template/test_vacuum.py @@ -15,7 +15,7 @@ from homeassistant.components.vacuum import ( from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import entity_registry as er +from homeassistant.helpers import entity_registry as er, issue_registry as ir from homeassistant.helpers.entity_component import async_update_entity from homeassistant.setup import async_setup_component @@ -589,6 +589,37 @@ async def test_battery_level_template( _verify(hass, STATE_UNKNOWN, expected) +@pytest.mark.parametrize( + ("count", "state_template", "extra_config", "attribute_template"), + [(1, "{{ states('sensor.test_state') }}", {}, "{{ 50 }}")], +) +@pytest.mark.parametrize( + ("style", "attribute"), + [ + (ConfigurationStyle.LEGACY, "battery_level_template"), + (ConfigurationStyle.MODERN, "battery_level"), + (ConfigurationStyle.TRIGGER, "battery_level"), + ], +) +@pytest.mark.usefixtures("setup_single_attribute_state_vacuum") +async def test_battery_level_template_repair( + hass: HomeAssistant, issue_registry: ir.IssueRegistry +) -> None: + """Test battery_level template raises issue.""" + # Ensure trigger entity templates are rendered + hass.states.async_set(TEST_STATE_SENSOR, VacuumActivity.DOCKED) + await hass.async_block_till_done() + + assert len(issue_registry.issues) == 1 + issue = issue_registry.async_get_issue( + "template", f"deprecated_battery_level_{TEST_ENTITY_ID}" + ) + assert issue.domain == "template" + assert issue.severity == ir.IssueSeverity.WARNING + assert issue.translation_placeholders["entity_name"] == TEST_OBJECT_ID + assert issue.translation_placeholders["entity_id"] == TEST_ENTITY_ID + + @pytest.mark.parametrize( ("count", "state_template", "extra_config"), [