Fix entity and device selectors (#148580)

This commit is contained in:
Artur Pragacz 2025-07-15 18:58:42 +02:00 committed by GitHub
parent 8bd51a7fd1
commit 3e0628cec2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 14 deletions

View File

@ -15,9 +15,10 @@ generate_data:
required: false
selector:
entity:
domain: ai_task
supported_features:
- ai_task.AITaskEntityFeature.GENERATE_DATA
filter:
domain: ai_task
supported_features:
- ai_task.AITaskEntityFeature.GENERATE_DATA
structure:
advanced: true
required: false

View File

@ -68,9 +68,10 @@ ask_question:
required: true
selector:
entity:
domain: assist_satellite
supported_features:
- assist_satellite.AssistSatelliteEntityFeature.START_CONVERSATION
filter:
domain: assist_satellite
supported_features:
- assist_satellite.AssistSatelliteEntityFeature.START_CONVERSATION
question:
required: false
example: "What kind of music would you like to play?"

View File

@ -160,6 +160,22 @@ ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
)
# Legacy entity selector config schema used directly under entity selectors
# is provided for backwards compatibility and remains feature frozen.
# New filtering features should be added under the `filter` key instead.
# https://github.com/home-assistant/frontend/pull/15302
LEGACY_ENTITY_SELECTOR_CONFIG_SCHEMA = vol.Schema(
{
# Integration that provided the entity
vol.Optional("integration"): str,
# Domain the entity belongs to
vol.Optional("domain"): vol.All(cv.ensure_list, [str]),
# Device class of the entity
vol.Optional("device_class"): vol.All(cv.ensure_list, [str]),
}
)
class EntityFilterSelectorConfig(TypedDict, total=False):
"""Class to represent a single entity selector config."""
@ -179,10 +195,22 @@ DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
vol.Optional("model"): str,
# Model ID of device
vol.Optional("model_id"): str,
# Device has to contain entities matching this selector
vol.Optional("entity"): vol.All(
cv.ensure_list, [ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA]
),
}
)
# Legacy device selector config schema used directly under device selectors
# is provided for backwards compatibility and remains feature frozen.
# New filtering features should be added under the `filter` key instead.
# https://github.com/home-assistant/frontend/pull/15302
LEGACY_DEVICE_SELECTOR_CONFIG_SCHEMA = vol.Schema(
{
# Integration linked to it with a config entry
vol.Optional("integration"): str,
# Manufacturer of device
vol.Optional("manufacturer"): str,
# Model of device
vol.Optional("model"): str,
}
)
@ -714,9 +742,13 @@ class DeviceSelector(Selector[DeviceSelectorConfig]):
selector_type = "device"
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA.schema
LEGACY_DEVICE_SELECTOR_CONFIG_SCHEMA.schema
).extend(
{
# Device has to contain entities matching this selector
vol.Optional("entity"): vol.All(
cv.ensure_list, [ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA]
),
vol.Optional("multiple", default=False): cv.boolean,
vol.Optional("filter"): vol.All(
cv.ensure_list,
@ -794,7 +826,7 @@ class EntitySelector(Selector[EntitySelectorConfig]):
selector_type = "entity"
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA.schema
LEGACY_ENTITY_SELECTOR_CONFIG_SCHEMA.schema
).extend(
{
vol.Optional("exclude_entities"): [str],

View File

@ -88,7 +88,6 @@ def _test_selector(
({"integration": "zha"}, ("abc123",), (None,)),
({"manufacturer": "mock-manuf"}, ("abc123",), (None,)),
({"model": "mock-model"}, ("abc123",), (None,)),
({"model_id": "mock-model_id"}, ("abc123",), (None,)),
({"manufacturer": "mock-manuf", "model": "mock-model"}, ("abc123",), (None,)),
(
{"integration": "zha", "manufacturer": "mock-manuf", "model": "mock-model"},
@ -128,6 +127,7 @@ def _test_selector(
"integration": "zha",
"manufacturer": "mock-manuf",
"model": "mock-model",
"model_id": "mock-model_id",
}
},
("abc123",),
@ -140,11 +140,13 @@ def _test_selector(
"integration": "zha",
"manufacturer": "mock-manuf",
"model": "mock-model",
"model_id": "mock-model_id",
},
{
"integration": "matter",
"manufacturer": "other-mock-manuf",
"model": "other-mock-model",
"model_id": "other-mock-model_id",
},
]
},
@ -158,6 +160,19 @@ def test_device_selector_schema(schema, valid_selections, invalid_selections) ->
_test_selector("device", schema, valid_selections, invalid_selections)
@pytest.mark.parametrize(
"schema",
[
# model_id should be used under the filter key
{"model_id": "mock-model_id"},
],
)
def test_device_selector_schema_error(schema) -> None:
"""Test device selector."""
with pytest.raises(vol.Invalid):
selector.validate_selector({"device": schema})
@pytest.mark.parametrize(
("schema", "valid_selections", "invalid_selections"),
[
@ -290,10 +305,12 @@ def test_entity_selector_schema(schema, valid_selections, invalid_selections) ->
{"filter": [{"supported_features": ["light.FooEntityFeature.blah"]}]},
# Unknown feature enum member
{"filter": [{"supported_features": ["light.LightEntityFeature.blah"]}]},
# supported_features should be used under the filter key
{"supported_features": ["light.LightEntityFeature.EFFECT"]},
],
)
def test_entity_selector_schema_error(schema) -> None:
"""Test number selector."""
"""Test entity selector."""
with pytest.raises(vol.Invalid):
selector.validate_selector({"entity": schema})