Restore attributes of template binary sensor (#69350)
parent
8c794ecf93
commit
8174b831cf
|
@ -331,6 +331,7 @@ class TriggerBinarySensorEntity(TriggerEntity, BinarySensorEntity, RestoreEntity
|
||||||
and self._state is None
|
and self._state is None
|
||||||
):
|
):
|
||||||
self._state = last_state.state == STATE_ON
|
self._state = last_state.state == STATE_ON
|
||||||
|
self.restore_attributes(last_state)
|
||||||
|
|
||||||
if CONF_AUTO_OFF not in self._config:
|
if CONF_AUTO_OFF not in self._config:
|
||||||
return
|
return
|
||||||
|
|
|
@ -4,8 +4,16 @@ from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_ICON, CONF_NAME, CONF_UNIQUE_ID
|
from homeassistant.const import (
|
||||||
from homeassistant.core import HomeAssistant, callback
|
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.exceptions import TemplateError
|
||||||
from homeassistant.helpers import template
|
from homeassistant.helpers import template
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
@ -13,6 +21,12 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
from . import TriggerUpdateCoordinator
|
from . import TriggerUpdateCoordinator
|
||||||
from .const import CONF_ATTRIBUTES, CONF_AVAILABILITY, CONF_PICTURE
|
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]):
|
class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]):
|
||||||
"""Template entity based on trigger data."""
|
"""Template entity based on trigger data."""
|
||||||
|
@ -45,10 +59,10 @@ class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]):
|
||||||
self._to_render_complex: list[str] = []
|
self._to_render_complex: list[str] = []
|
||||||
|
|
||||||
for itm in (
|
for itm in (
|
||||||
CONF_NAME,
|
|
||||||
CONF_ICON,
|
|
||||||
CONF_PICTURE,
|
|
||||||
CONF_AVAILABILITY,
|
CONF_AVAILABILITY,
|
||||||
|
CONF_ICON,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_PICTURE,
|
||||||
):
|
):
|
||||||
if itm not in config:
|
if itm not in config:
|
||||||
continue
|
continue
|
||||||
|
@ -115,6 +129,21 @@ class TriggerEntity(CoordinatorEntity[TriggerUpdateCoordinator]):
|
||||||
if self.coordinator.data is not None:
|
if self.coordinator.data is not None:
|
||||||
self._process_data()
|
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
|
@callback
|
||||||
def _process_data(self) -> None:
|
def _process_data(self) -> None:
|
||||||
"""Process new data."""
|
"""Process new data."""
|
||||||
|
|
|
@ -1142,29 +1142,41 @@ async def test_template_with_trigger_templated_delay_on(hass, start_ha):
|
||||||
"name": "test",
|
"name": "test",
|
||||||
"state": "{{ trigger.event.data.beer == 2 }}",
|
"state": "{{ trigger.event.data.beer == 2 }}",
|
||||||
"device_class": "motion",
|
"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(
|
@pytest.mark.parametrize(
|
||||||
"restored_state, initial_state",
|
"restored_state, initial_state, initial_attributes",
|
||||||
[
|
[
|
||||||
(ON, ON),
|
(ON, ON, ["entity_picture", "icon", "plus_one"]),
|
||||||
(OFF, OFF),
|
(OFF, OFF, ["entity_picture", "icon", "plus_one"]),
|
||||||
(STATE_UNAVAILABLE, STATE_UNKNOWN),
|
(STATE_UNAVAILABLE, STATE_UNKNOWN, []),
|
||||||
(STATE_UNKNOWN, STATE_UNKNOWN),
|
(STATE_UNKNOWN, STATE_UNKNOWN, []),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_trigger_entity_restore_state(
|
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."""
|
"""Test restoring trigger template binary sensor."""
|
||||||
|
|
||||||
|
restored_attributes = {
|
||||||
|
"entity_picture": "/local/cats.png",
|
||||||
|
"icon": "mdi:ship",
|
||||||
|
"plus_one": 55,
|
||||||
|
}
|
||||||
|
|
||||||
fake_state = State(
|
fake_state = State(
|
||||||
"binary_sensor.test",
|
"binary_sensor.test",
|
||||||
restored_state,
|
restored_state,
|
||||||
{},
|
restored_attributes,
|
||||||
)
|
)
|
||||||
fake_extra_data = {
|
fake_extra_data = {
|
||||||
"auto_off_time": None,
|
"auto_off_time": None,
|
||||||
|
@ -1183,6 +1195,22 @@ async def test_trigger_entity_restore_state(
|
||||||
|
|
||||||
state = hass.states.get("binary_sensor.test")
|
state = hass.states.get("binary_sensor.test")
|
||||||
assert state.state == initial_state
|
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")])
|
@pytest.mark.parametrize("count,domain", [(1, "template")])
|
||||||
|
|
Loading…
Reference in New Issue