Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 18 additions & 20 deletions homeassistant/helpers/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ class Selector[_T: Mapping[str, Any]]:
# context for filtering for example. The selector defines
# which context keys it supports and what selector types
# are allowed for each key.
allowed_context_keys: dict[str, set[str]] = {}
allowed_context_keys: dict[str, set[str]]

def __init__(self, config: Mapping[str, Any] | None = None) -> None:
"""Instantiate a selector."""
self.config = self.CONFIG_SCHEMA(config)
self.allowed_context_keys = {}
Comment thread
jbouwh marked this conversation as resolved.

@override
def __eq__(self, other: object) -> bool:
Expand Down Expand Up @@ -427,11 +428,6 @@ class AttributeSelector(Selector[AttributeSelectorConfig]):

selector_type = "attribute"

allowed_context_keys = {
# Filters the available attributes based on the selected entity
"filter_entity": {"entity"}
}

CONFIG_SCHEMA = make_selector_config_schema(
{
vol.Required("entity_id"): cv.entity_id,
Expand All @@ -444,6 +440,10 @@ class AttributeSelector(Selector[AttributeSelectorConfig]):
def __init__(self, config: AttributeSelectorConfig) -> None:
"""Instantiate a selector."""
super().__init__(config)
self.allowed_context_keys = {
# Filters the available attributes based on the selected entity
"filter_entity": {"entity"}
}

def __call__(self, data: Any) -> str:
"""Validate the passed selection."""
Expand Down Expand Up @@ -1354,11 +1354,6 @@ class MediaSelector(Selector[MediaSelectorConfig]):

selector_type = "media"

allowed_context_keys = {
# Filters the available media based on the selected entity
"filter_entity": {EntitySelector.selector_type}
}

CONFIG_SCHEMA = make_selector_config_schema(
{
vol.Optional("accept"): [str],
Expand All @@ -1381,6 +1376,10 @@ class MediaSelector(Selector[MediaSelectorConfig]):
def __init__(self, config: MediaSelectorConfig | None = None) -> None:
"""Instantiate a selector."""
super().__init__(config)
self.allowed_context_keys = {
# Filters the available media based on the selected entity
"filter_entity": {EntitySelector.selector_type}
}

def __call__(self, data: Any) -> dict[str, Any] | list[dict[str, Any]]:
"""Validate the passed selection."""
Expand Down Expand Up @@ -2035,15 +2034,6 @@ class StateSelector(Selector[StateSelectorConfig]):

selector_type = "state"

allowed_context_keys = {
# Filters the available states based on the selected entity
"filter_entity": {EntitySelector.selector_type},
# Filters the available states based on the selected target
"filter_target": {"target"},
# Only show the attribute values of a specific attribute
"filter_attribute": {AttributeSelector.selector_type},
}

CONFIG_SCHEMA = make_selector_config_schema(
{
vol.Optional("entity_id"): cv.entity_id,
Expand All @@ -2056,6 +2046,14 @@ class StateSelector(Selector[StateSelectorConfig]):
def __init__(self, config: StateSelectorConfig) -> None:
"""Instantiate a selector."""
super().__init__(config)
self.allowed_context_keys = {
# Filters the available states based on the selected entity
"filter_entity": {EntitySelector.selector_type},
# Filters the available states based on the selected target
"filter_target": {"target"},
# Only show the attribute values of a specific attribute
"filter_attribute": {AttributeSelector.selector_type},
}

def __call__(self, data: Any) -> str | list[str]:
"""Validate the passed selection."""
Expand Down
29 changes: 29 additions & 0 deletions tests/helpers/test_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,35 @@ def test_invalid_base_schema(schema) -> None:
selector.validate_selector(schema)


def test_allowed_context_keys_not_mutable() -> None:
"""Test the allowed_context_keys attribute is not mutable."""

class TestSelectorConfig(selector.BaseSelectorConfig, total=False):
"""Test selector config class."""

@selector.SELECTORS.register("test")
class TestSelector(selector.Selector):
Comment thread
Copilot marked this conversation as resolved.
Outdated
"""Test selector to test allowed_context_keys attribute is not mutable."""

CONFIG_SCHEMA = selector.make_selector_config_schema({})

selector_type = "test"

def __init__(self, config: TestSelectorConfig | None = None) -> None:
"""Test mutation fails."""
super().__init__(config)
Comment thread
Copilot marked this conversation as resolved.
Outdated

def __call__(self, data: Any) -> Any:
"""Validate the passed selection."""
return data

test_selector = TestSelector(TestSelectorConfig())
other_selector = TestSelector(TestSelectorConfig())
test_selector.allowed_context_keys["some_key"] = set()
assert test_selector.allowed_context_keys
assert not other_selector.allowed_context_keys


def _test_selector(
selector_type: str,
schema: dict | None,
Expand Down
Loading