From 98df5f5f0c227dffb2fd66f26de157b8e2f46a27 Mon Sep 17 00:00:00 2001 From: Artur Pragacz <49985303+arturpragacz@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:20:11 +0200 Subject: [PATCH] Validate selectors in the condition helper (#151884) --- homeassistant/helpers/condition.py | 9 +++- tests/helpers/test_condition.py | 75 +++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index d9f16217c2e2..67c99eb70b47 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -30,6 +30,7 @@ from homeassistant.const import ( CONF_FOR, CONF_ID, CONF_MATCH, + CONF_SELECTOR, CONF_STATE, CONF_VALUE_TEMPLATE, CONF_WEEKDAY, @@ -59,9 +60,10 @@ from homeassistant.util.async_ import run_callback_threadsafe from homeassistant.util.hass_dict import HassKey from homeassistant.util.yaml import load_yaml_dict -from . import config_validation as cv, entity_registry as er +from . import config_validation as cv, entity_registry as er, selector from .automation import get_absolute_description_key, get_relative_description_key from .integration_platform import async_process_integration_platforms +from .selector import TargetSelector from .template import Template, render_complex from .trace import ( TraceElement, @@ -110,12 +112,15 @@ CONDITIONS: HassKey[dict[str, str]] = HassKey("conditions") # Basic schemas to sanity check the condition descriptions, # full validation is done by hassfest.conditions _FIELD_SCHEMA = vol.Schema( - {}, + { + vol.Optional(CONF_SELECTOR): selector.validate_selector, + }, extra=vol.ALLOW_EXTRA, ) _CONDITION_SCHEMA = vol.Schema( { + vol.Optional("target"): TargetSelector.CONFIG_SCHEMA, vol.Optional("fields"): vol.Schema({str: _FIELD_SCHEMA}), }, extra=vol.ALLOW_EXTRA, diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index b037d6a450ee..fef476556dc8 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -2385,7 +2385,15 @@ async def test_async_get_all_descriptions( ) -> None: """Test async_get_all_descriptions.""" device_automation_condition_descriptions = """ - _device: {} + _device: + fields: + entity: + selector: + entity: + filter: + domain: alarm_control_panel + supported_features: + - alarm_control_panel.AlarmControlPanelEntityFeature.ARM_HOME """ assert await async_setup_component(hass, DOMAIN_SUN, {}) @@ -2427,14 +2435,28 @@ async def test_async_get_all_descriptions( "fields": { "after": { "example": "sunrise", - "selector": {"select": {"options": ["sunrise", "sunset"]}}, + "selector": { + "select": { + "custom_value": False, + "multiple": False, + "options": ["sunrise", "sunset"], + "sort": False, + } + }, }, - "after_offset": {"selector": {"time": None}}, + "after_offset": {"selector": {"time": {}}}, "before": { "example": "sunrise", - "selector": {"select": {"options": ["sunrise", "sunset"]}}, + "selector": { + "select": { + "custom_value": False, + "multiple": False, + "options": ["sunrise", "sunset"], + "sort": False, + } + }, }, - "before_offset": {"selector": {"time": None}}, + "before_offset": {"selector": {"time": {}}}, } } } @@ -2456,21 +2478,50 @@ async def test_async_get_all_descriptions( new_descriptions = await condition.async_get_all_descriptions(hass) assert new_descriptions is not descriptions assert new_descriptions == { - "device": { - "fields": {}, - }, "sun": { "fields": { "after": { "example": "sunrise", - "selector": {"select": {"options": ["sunrise", "sunset"]}}, + "selector": { + "select": { + "custom_value": False, + "multiple": False, + "options": ["sunrise", "sunset"], + "sort": False, + } + }, }, - "after_offset": {"selector": {"time": None}}, + "after_offset": {"selector": {"time": {}}}, "before": { "example": "sunrise", - "selector": {"select": {"options": ["sunrise", "sunset"]}}, + "selector": { + "select": { + "custom_value": False, + "multiple": False, + "options": ["sunrise", "sunset"], + "sort": False, + } + }, + }, + "before_offset": {"selector": {"time": {}}}, + } + }, + "device": { + "fields": { + "entity": { + "selector": { + "entity": { + "filter": [ + { + "domain": ["alarm_control_panel"], + "supported_features": [1], + } + ], + "multiple": False, + "reorder": False, + }, + }, }, - "before_offset": {"selector": {"time": None}}, } }, }