Allow nested schema validation in event automation trigger (#126771)

* Allow nested schema validation in event automation trigger

* Fix rfxtrx device trigger

* Don't create nested voluptuous schemas

* Fix editing mistake

* Fix format

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Sören Beye 2025-05-26 17:34:13 +02:00 committed by GitHub
parent c14d17f88c
commit 3dc7b75e4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 55 additions and 6 deletions

View File

@ -75,14 +75,18 @@ async def async_attach_trigger(
event_data.update(
template.render_complex(config[CONF_EVENT_DATA], variables, limited=True)
)
# Build the schema or a an items view if the schema is simple
# and does not contain sub-dicts. We explicitly do not check for
# list like the context data below since lists are a special case
# only for context data. (see test test_event_data_with_list)
# For performance reasons, we want to avoid using a voluptuous schema here
# unless required. Thus, if possible, we try to use a simple items comparison
# For that, we explicitly do not check for list like the context data below
# since lists are a special case only used for context data, see test
# test_event_data_with_list. Otherwise, we build a volutupus schema, see test
# test_event_data_with_list_nested
if any(isinstance(value, dict) for value in event_data.values()):
event_data_schema = vol.Schema(
{vol.Required(key): value for key, value in event_data.items()},
event_data,
extra=vol.ALLOW_EXTRA,
required=True,
)
else:
# Use a simple items comparison if possible

View File

@ -97,7 +97,7 @@ async def async_attach_trigger(
if config[CONF_TYPE] == CONF_TYPE_COMMAND:
event_data["values"] = {"Command": config[CONF_SUBTYPE]}
elif config[CONF_TYPE] == CONF_TYPE_STATUS:
event_data["values"] = {"Status": config[CONF_SUBTYPE]}
event_data["values"] = {"Sensor Status": config[CONF_SUBTYPE]}
event_config = event_trigger.TRIGGER_SCHEMA(
{

View File

@ -517,6 +517,51 @@ async def test_event_data_with_list(
await hass.async_block_till_done()
assert len(service_calls) == 1
# don't match if property doesn't exist at all
hass.bus.async_fire("test_event", {"other_attr": [1, 2]})
await hass.async_block_till_done()
assert len(service_calls) == 1
async def test_event_data_with_list_nested(
hass: HomeAssistant, service_calls: list[ServiceCall]
) -> None:
"""Test the (non)firing of event when the data schema has nested lists."""
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "event",
"event_type": "test_event",
"event_data": {"service_data": {"some_attr": [1, 2]}},
"context": {},
},
"action": {"service": "test.automation"},
}
},
)
hass.bus.async_fire("test_event", {"service_data": {"some_attr": [1, 2]}})
await hass.async_block_till_done()
assert len(service_calls) == 1
# don't match a single value
hass.bus.async_fire("test_event", {"service_data": {"some_attr": 1}})
await hass.async_block_till_done()
assert len(service_calls) == 1
# don't match a containing list
hass.bus.async_fire("test_event", {"service_data": {"some_attr": [1, 2, 3]}})
await hass.async_block_till_done()
assert len(service_calls) == 1
# don't match if property doesn't exist at all
hass.bus.async_fire("test_event", {"service_data": {"other_attr": [1, 2]}})
await hass.async_block_till_done()
assert len(service_calls) == 1
@pytest.mark.parametrize(
"event_type", ["state_reported", ["test_event", "state_reported"]]