Skip to content

Commit 8406576

Browse files
authored
Validate template binary sensor auto off has trigger (home-assistant#153953)
1 parent e7c60f5 commit 8406576

File tree

2 files changed

+142
-1
lines changed

2 files changed

+142
-1
lines changed

homeassistant/components/template/config.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from homeassistant.helpers import config_validation as cv, issue_registry as ir
4848
from homeassistant.helpers.condition import async_validate_conditions_config
4949
from homeassistant.helpers.issue_registry import IssueSeverity
50+
from homeassistant.helpers.template import Template
5051
from homeassistant.helpers.trigger import async_validate_trigger_config
5152
from homeassistant.helpers.typing import ConfigType
5253
from homeassistant.setup import async_notify_setup_error
@@ -70,14 +71,39 @@
7071
vacuum as vacuum_platform,
7172
weather as weather_platform,
7273
)
73-
from .const import DOMAIN, PLATFORMS, TemplateConfig
74+
from .const import CONF_DEFAULT_ENTITY_ID, DOMAIN, PLATFORMS, TemplateConfig
7475
from .helpers import async_get_blueprints, rewrite_legacy_to_modern_configs
7576

7677
_LOGGER = logging.getLogger(__name__)
7778

7879
PACKAGE_MERGE_HINT = "list"
7980

8081

82+
def validate_binary_sensor_auto_off_has_trigger(obj: dict) -> dict:
83+
"""Validate that binary sensors with auto_off have triggers."""
84+
if CONF_TRIGGERS not in obj and DOMAIN_BINARY_SENSOR in obj:
85+
binary_sensors: list[ConfigType] = obj[DOMAIN_BINARY_SENSOR]
86+
for binary_sensor in binary_sensors:
87+
if binary_sensor_platform.CONF_AUTO_OFF in binary_sensor:
88+
identifier = f"{CONF_NAME}: {binary_sensor_platform.DEFAULT_NAME}"
89+
if (
90+
(name := binary_sensor.get(CONF_NAME))
91+
and isinstance(name, Template)
92+
and name.template != binary_sensor_platform.DEFAULT_NAME
93+
):
94+
identifier = f"{CONF_NAME}: {name.template}"
95+
elif default_entity_id := binary_sensor.get(CONF_DEFAULT_ENTITY_ID):
96+
identifier = f"{CONF_DEFAULT_ENTITY_ID}: {default_entity_id}"
97+
elif unique_id := binary_sensor.get(CONF_UNIQUE_ID):
98+
identifier = f"{CONF_UNIQUE_ID}: {unique_id}"
99+
100+
raise vol.Invalid(
101+
f"The auto_off option for template binary sensor: {identifier} requires a trigger, remove the auto_off option or rewrite configuration to use a trigger"
102+
)
103+
104+
return obj
105+
106+
81107
def ensure_domains_do_not_have_trigger_or_action(*keys: str) -> Callable[[dict], dict]:
82108
"""Validate that config does not contain trigger and action."""
83109
domains = set(keys)
@@ -211,6 +237,7 @@ def _backward_compat_schema(value: Any | None) -> Any:
211237
ensure_domains_do_not_have_trigger_or_action(
212238
DOMAIN_BUTTON,
213239
),
240+
validate_binary_sensor_auto_off_has_trigger,
214241
)
215242

216243
TEMPLATE_BLUEPRINT_SCHEMA = vol.All(

tests/components/template/test_config.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,120 @@ async def test_invalid_default_entity_id(
102102
CONFIG_SECTION_SCHEMA(config)
103103

104104

105+
@pytest.mark.parametrize(
106+
("config", "expected_error"),
107+
[
108+
(
109+
{
110+
"trigger": {"trigger": "event", "event_type": "my_event"},
111+
"binary_sensor": {
112+
"state": "{{ states('binary_sensor.test') }}",
113+
"unique_id": "test",
114+
"name": "test",
115+
"auto_off": "00:00:01",
116+
},
117+
},
118+
None,
119+
),
120+
(
121+
{
122+
"binary_sensor": {
123+
"state": "{{ states('binary_sensor.test') }}",
124+
"name": "test",
125+
},
126+
},
127+
None,
128+
),
129+
(
130+
{
131+
"binary_sensor": {
132+
"state": "{{ states('binary_sensor.test') }}",
133+
"auto_off": "00:00:01",
134+
},
135+
},
136+
"The auto_off option for template binary sensor: name: Template Binary Sensor",
137+
),
138+
(
139+
{
140+
"binary_sensor": {
141+
"state": "{{ states('binary_sensor.test') }}",
142+
"name": "test",
143+
"auto_off": "00:00:01",
144+
},
145+
},
146+
"The auto_off option for template binary sensor: name: test",
147+
),
148+
(
149+
{
150+
"binary_sensor": {
151+
"state": "{{ states('binary_sensor.test') }}",
152+
"unique_id": "test_unique_id",
153+
"auto_off": "00:00:01",
154+
},
155+
},
156+
"The auto_off option for template binary sensor: unique_id: test_unique_id",
157+
),
158+
(
159+
{
160+
"binary_sensor": {
161+
"state": "{{ states('binary_sensor.test') }}",
162+
"name": "test",
163+
"unique_id": "test_unique_id",
164+
"auto_off": "00:00:01",
165+
},
166+
},
167+
"The auto_off option for template binary sensor: name: test",
168+
),
169+
(
170+
{
171+
"binary_sensor": {
172+
"state": "{{ states('binary_sensor.test') }}",
173+
"default_entity_id": "binary_sensor.test_entity_id",
174+
"auto_off": "00:00:01",
175+
},
176+
},
177+
"The auto_off option for template binary sensor: default_entity_id: binary_sensor.test_entity_id",
178+
),
179+
(
180+
{
181+
"binary_sensor": {
182+
"state": "{{ states('binary_sensor.test') }}",
183+
"name": "test",
184+
"unique_id": "test_unique_id",
185+
"default_entity_id": "binary_sensor.test_entity_id",
186+
"auto_off": "00:00:01",
187+
},
188+
},
189+
"The auto_off option for template binary sensor: name: test",
190+
),
191+
(
192+
{
193+
"binary_sensor": {
194+
"state": "{{ states('binary_sensor.test') }}",
195+
"unique_id": "test_unique_id",
196+
"default_entity_id": "binary_sensor.test_entity_id",
197+
"auto_off": "00:00:01",
198+
},
199+
},
200+
"The auto_off option for template binary sensor: default_entity_id: binary_sensor.test_entity_id",
201+
),
202+
],
203+
)
204+
async def test_invalid_binary_sensor_schema_with_auto_off(
205+
hass: HomeAssistant,
206+
config: dict,
207+
expected_error: str | None,
208+
caplog: pytest.LogCaptureFixture,
209+
) -> None:
210+
"""Test invalid config schemas create issue and log warning."""
211+
212+
await async_setup_component(hass, "template", {"template": [config]})
213+
214+
assert (
215+
expected_error is None and "ERROR" not in caplog.text
216+
) or expected_error in caplog.text
217+
218+
105219
@pytest.mark.parametrize(
106220
("config", "expected"),
107221
[

0 commit comments

Comments
 (0)