Skip to content

Commit 312812d

Browse files
authored
Fix variables in icon, picture, and name for state based template entities (home-assistant#154994)
1 parent e0d4044 commit 312812d

File tree

4 files changed

+180
-1
lines changed

4 files changed

+180
-1
lines changed

homeassistant/components/template/template_entity.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,13 @@ def name(self) -> str:
200200
"""Name of this state."""
201201
return "<None>"
202202

203-
variables = {"this": DummyState()}
203+
# Render the current variables and add a dummy this variable to them.
204+
variables = (
205+
self._run_variables
206+
if isinstance(self._run_variables, dict)
207+
else self._run_variables.async_render(self.hass, {})
208+
)
209+
variables = {"this": DummyState(), **variables}
204210

205211
# Try to render the name as it can influence the entity ID
206212
self._attr_name = None

tests/components/template/test_blueprint.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,90 @@ async def test_reload_template_when_blueprint_changes(hass: HomeAssistant) -> No
228228
assert not_inverted.state == "on"
229229

230230

231+
async def test_init_attribute_variables_from_blueprint(hass: HomeAssistant) -> None:
232+
"""Test a state based blueprint initializes icon, name, and picture with variables."""
233+
blueprint = "test_init_attribute_variables.yaml"
234+
source = "switch.foo"
235+
entity_id = "sensor.foo"
236+
hass.states.async_set(source, "on", {"friendly_name": "Foo"})
237+
config = {
238+
DOMAIN: [
239+
{
240+
"use_blueprint": {
241+
"path": blueprint,
242+
"input": {"switch": source},
243+
},
244+
}
245+
],
246+
}
247+
assert await async_setup_component(
248+
hass,
249+
DOMAIN,
250+
config,
251+
)
252+
await hass.async_block_till_done()
253+
254+
# Check initial state
255+
sensor = hass.states.get(entity_id)
256+
assert sensor
257+
assert sensor.state == "True"
258+
assert sensor.attributes["icon"] == "mdi:lightbulb"
259+
assert sensor.attributes["entity_picture"] == "on.png"
260+
assert sensor.attributes["friendly_name"] == "Foo"
261+
assert sensor.attributes["extra"] == "ab"
262+
263+
hass.states.async_set(source, "off", {"friendly_name": "Foo"})
264+
await hass.async_block_till_done()
265+
266+
# Check to see that the template light works
267+
sensor = hass.states.get(entity_id)
268+
assert sensor
269+
assert sensor.state == "False"
270+
assert sensor.attributes["icon"] == "mdi:lightbulb-off"
271+
assert sensor.attributes["entity_picture"] == "off.png"
272+
assert sensor.attributes["friendly_name"] == "Foo"
273+
assert sensor.attributes["extra"] == "ab"
274+
275+
# Reload the templates without any change, but with updated blueprint
276+
blueprint_config = yaml_util.load_yaml(
277+
pathlib.Path("tests/testing_config/blueprints/template/") / blueprint
278+
)
279+
blueprint_config["variables"]["extraa"] = "c"
280+
blueprint_config["sensor"]["variables"]["extrab"] = "d"
281+
with (
282+
patch(
283+
"homeassistant.config.load_yaml_config_file",
284+
autospec=True,
285+
return_value=config,
286+
),
287+
patch(
288+
"homeassistant.components.blueprint.models.yaml_util.load_yaml_dict",
289+
autospec=True,
290+
return_value=blueprint_config,
291+
),
292+
):
293+
await hass.services.async_call(DOMAIN, SERVICE_RELOAD, blocking=True)
294+
295+
sensor = hass.states.get(entity_id)
296+
assert sensor
297+
assert sensor.state == "False"
298+
assert sensor.attributes["icon"] == "mdi:lightbulb-off"
299+
assert sensor.attributes["entity_picture"] == "off.png"
300+
assert sensor.attributes["friendly_name"] == "Foo"
301+
assert sensor.attributes["extra"] == "cd"
302+
303+
hass.states.async_set(source, "on", {"friendly_name": "Foo"})
304+
await hass.async_block_till_done()
305+
306+
sensor = hass.states.get(entity_id)
307+
assert sensor
308+
assert sensor.state == "True"
309+
assert sensor.attributes["icon"] == "mdi:lightbulb"
310+
assert sensor.attributes["entity_picture"] == "on.png"
311+
assert sensor.attributes["friendly_name"] == "Foo"
312+
assert sensor.attributes["extra"] == "cd"
313+
314+
231315
@pytest.mark.parametrize(
232316
("blueprint"),
233317
["test_event_sensor.yaml", "test_event_sensor_legacy_schema.yaml"],

tests/components/template/test_config.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
import pytest
66
import voluptuous as vol
77

8+
from homeassistant.components.template import DOMAIN
89
from homeassistant.components.template.config import (
910
CONFIG_SECTION_SCHEMA,
1011
async_validate_config_section,
1112
)
1213
from homeassistant.core import HomeAssistant
1314
from homeassistant.helpers.script_variables import ScriptVariables
1415
from homeassistant.helpers.template import Template
16+
from homeassistant.setup import async_setup_component
1517

1618

1719
@pytest.mark.parametrize(
@@ -256,3 +258,59 @@ async def test_combined_trigger_variables(
256258
assert root_variables.as_dict() == expected_root
257259
variables: ScriptVariables = validated["binary_sensor"][0].get("variables", empty)
258260
assert variables.as_dict() == expected_entity
261+
262+
263+
async def test_state_init_attribute_variables(
264+
hass: HomeAssistant,
265+
) -> None:
266+
"""Test a state based template entity initializes icon, name, and picture with variables."""
267+
source = "switch.foo"
268+
entity_id = "sensor.foo"
269+
270+
hass.states.async_set(source, "on", {"friendly_name": "Foo"})
271+
config = {
272+
"template": [
273+
{
274+
"variables": {
275+
"switch": "switch.foo",
276+
"on_icon": "mdi:lightbulb",
277+
"on_picture": "on.png",
278+
},
279+
"sensor": {
280+
"variables": {
281+
"off_icon": "mdi:lightbulb-off",
282+
"off_picture": "off.png",
283+
},
284+
"name": "{{ state_attr(switch, 'friendly_name') }}",
285+
"icon": "{{ on_icon if is_state(switch, 'on') else off_icon }}",
286+
"picture": "{{ on_picture if is_state(switch, 'on') else off_picture }}",
287+
"state": "{{ is_state(switch, 'on') }}",
288+
},
289+
}
290+
],
291+
}
292+
assert await async_setup_component(
293+
hass,
294+
DOMAIN,
295+
config,
296+
)
297+
await hass.async_block_till_done()
298+
299+
# Check initial state
300+
sensor = hass.states.get(entity_id)
301+
assert sensor
302+
assert sensor.state == "True"
303+
assert sensor.attributes["icon"] == "mdi:lightbulb"
304+
assert sensor.attributes["entity_picture"] == "on.png"
305+
assert sensor.attributes["friendly_name"] == "Foo"
306+
307+
hass.states.async_set(source, "off", {"friendly_name": "Foo"})
308+
await hass.async_block_till_done()
309+
310+
# Check to see that the template light works
311+
sensor = hass.states.get(entity_id)
312+
assert sensor
313+
assert sensor.state == "False"
314+
assert sensor.attributes["icon"] == "mdi:lightbulb-off"
315+
assert sensor.attributes["entity_picture"] == "off.png"
316+
assert sensor.attributes["friendly_name"] == "Foo"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
blueprint:
2+
name: Switch to light
3+
domain: template
4+
input:
5+
switch:
6+
name: Switch
7+
description: The switch which should be converted
8+
selector:
9+
entity:
10+
multiple: false
11+
filter:
12+
- domain: switch
13+
default: null
14+
15+
variables:
16+
switch: !input switch
17+
on_icon: mdi:lightbulb
18+
on_picture: "on.png"
19+
extraa: "a"
20+
21+
sensor:
22+
variables:
23+
off_icon: mdi:lightbulb-off
24+
off_picture: "off.png"
25+
extrab: "b"
26+
name: "{{ state_attr(switch, 'friendly_name') }}"
27+
icon: "{{ on_icon if is_state(switch, 'on') else off_icon }}"
28+
picture: "{{ on_picture if is_state(switch, 'on') else off_picture }}"
29+
state: "{{ is_state(switch, 'on') }}"
30+
attributes:
31+
extra: "{{ extraa ~ extrab }}"

0 commit comments

Comments
 (0)