diff --git a/homeassistant/components/template/binary_sensor.py b/homeassistant/components/template/binary_sensor.py index 53267b29341..2a537e2aa6b 100644 --- a/homeassistant/components/template/binary_sensor.py +++ b/homeassistant/components/template/binary_sensor.py @@ -331,6 +331,7 @@ class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity and self._state is None ): self._state = last_state.state == STATE_ON + self.restore_attributes(last_state) if CONF_AUTO_OFF not in self._config: return diff --git a/homeassistant/components/template/trigger_entity.py b/homeassistant/components/template/trigger_entity.py index bebe941fbf9..6780d12c507 100644 --- a/homeassistant/components/template/trigger_entity.py +++ b/homeassistant/components/template/trigger_entity.py @@ -4,8 +4,16 @@ from __future__ import annotations import logging from typing import Any -from homeassistant.const import CONF_DEVICE_CLASS, CONF_ICON, CONF_NAME, CONF_UNIQUE_ID -from homeassistant.core import HomeAssistant, callback +from homeassistant.const import ( + ATTR_ENTITY_PICTURE, + ATTR_FRIENDLY_NAME, + ATTR_ICON, + CONF_DEVICE_CLASS, + CONF_ICON, + CONF_NAME, + CONF_UNIQUE_ID, +) +from homeassistant.core import HomeAssistant, State, callback from homeassistant.exceptions import TemplateError from homeassistant.helpers import template from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -13,6 +21,12 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import TriggerUpdateCoordinator from .const import CONF_ATTRIBUTES, CONF_AVAILABILITY, CONF_PICTURE +CONF_TO_ATTRIBUTE = { + CONF_ICON: ATTR_ICON, + CONF_NAME: ATTR_FRIENDLY_NAME, + CONF_PICTURE: ATTR_ENTITY_PICTURE, +} + class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]): """Template entity based on trigger data.""" @@ -45,10 +59,10 @@ class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]): self._to_render_complex: list[str] = [] for itm in ( - CONF_NAME, - CONF_ICON, - CONF_PICTURE, CONF_AVAILABILITY, + CONF_ICON, + CONF_NAME, + CONF_PICTURE, ): if itm not in config: continue @@ -115,6 +129,21 @@ class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]): if self.coordinator.data is not None: self._process_data() + def restore_attributes(self, last_state: State) -> None: + """Restore attributes.""" + for conf_key, attr in CONF_TO_ATTRIBUTE.items(): + if conf_key not in self._config or attr not in last_state.attributes: + continue + self._rendered[conf_key] = last_state.attributes[attr] + + if CONF_ATTRIBUTES in self._config: + extra_state_attributes = {} + for attr in self._config[CONF_ATTRIBUTES]: + if attr not in last_state.attributes: + continue + extra_state_attributes[attr] = last_state.attributes[attr] + self._rendered[CONF_ATTRIBUTES] = extra_state_attributes + @callback def _process_data(self) -> None: """Process new data.""" diff --git a/tests/components/template/test_binary_sensor.py b/tests/components/template/test_binary_sensor.py index 4bd6c9ce305..8c408460a21 100644 --- a/tests/components/template/test_binary_sensor.py +++ b/tests/components/template/test_binary_sensor.py @@ -1142,29 +1142,41 @@ async def test_template_with_trigger_templated_delay_on(hass, start_ha): "name": "test", "state": "{{ trigger.event.data.beer == 2 }}", "device_class": "motion", + "picture": "{{ '/local/dogs.png' }}", + "icon": "{{ 'mdi:pirate' }}", + "attributes": { + "plus_one": "{{ trigger.event.data.beer + 1 }}", + "another": "{{ trigger.event.data.uno_mas or 1 }}", + }, }, }, }, ], ) @pytest.mark.parametrize( - "restored_state, initial_state", + "restored_state, initial_state, initial_attributes", [ - (ON, ON), - (OFF, OFF), - (STATE_UNAVAILABLE, STATE_UNKNOWN), - (STATE_UNKNOWN, STATE_UNKNOWN), + (ON, ON, ["entity_picture", "icon", "plus_one"]), + (OFF, OFF, ["entity_picture", "icon", "plus_one"]), + (STATE_UNAVAILABLE, STATE_UNKNOWN, []), + (STATE_UNKNOWN, STATE_UNKNOWN, []), ], ) async def test_trigger_entity_restore_state( - hass, count, domain, config, restored_state, initial_state + hass, count, domain, config, restored_state, initial_state, initial_attributes ): """Test restoring trigger template binary sensor.""" + restored_attributes = { + "entity_picture": "/local/cats.png", + "icon": "mdi:ship", + "plus_one": 55, + } + fake_state = State( "binary_sensor.test", restored_state, - {}, + restored_attributes, ) fake_extra_data = { "auto_off_time": None, @@ -1183,6 +1195,22 @@ async def test_trigger_entity_restore_state( state = hass.states.get("binary_sensor.test") assert state.state == initial_state + for attr in restored_attributes: + if attr in initial_attributes: + assert state.attributes[attr] == restored_attributes[attr] + else: + assert attr not in state.attributes + assert "another" not in state.attributes + + hass.bus.async_fire("test_event", {"beer": 2}) + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.test") + assert state.state == ON + assert state.attributes["icon"] == "mdi:pirate" + assert state.attributes["entity_picture"] == "/local/dogs.png" + assert state.attributes["plus_one"] == 3 + assert state.attributes["another"] == 1 @pytest.mark.parametrize("count,domain", [(1, "template")])