mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17:10 +00:00
Exclude own entity from group entity selector (#68782)
This commit is contained in:
parent
463cb8679f
commit
2c92d19058
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Mapping
|
from collections.abc import Callable, Mapping
|
||||||
|
from functools import partial
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -13,6 +14,8 @@ from homeassistant.helpers.helper_config_entry_flow import (
|
|||||||
HelperConfigFlowHandler,
|
HelperConfigFlowHandler,
|
||||||
HelperFlowFormStep,
|
HelperFlowFormStep,
|
||||||
HelperFlowMenuStep,
|
HelperFlowMenuStep,
|
||||||
|
HelperOptionsFlowHandler,
|
||||||
|
entity_selector_without_own_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import DOMAIN
|
from . import DOMAIN
|
||||||
@ -20,12 +23,17 @@ from .binary_sensor import CONF_ALL
|
|||||||
from .const import CONF_HIDE_MEMBERS
|
from .const import CONF_HIDE_MEMBERS
|
||||||
|
|
||||||
|
|
||||||
def basic_group_options_schema(domain: str) -> vol.Schema:
|
def basic_group_options_schema(
|
||||||
|
domain: str,
|
||||||
|
handler: HelperConfigFlowHandler | HelperOptionsFlowHandler,
|
||||||
|
options: dict[str, Any],
|
||||||
|
) -> vol.Schema:
|
||||||
"""Generate options schema."""
|
"""Generate options schema."""
|
||||||
|
handler = cast(HelperOptionsFlowHandler, handler)
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_ENTITIES): selector.selector(
|
vol.Required(CONF_ENTITIES): entity_selector_without_own_entities(
|
||||||
{"entity": {"domain": domain, "multiple": True}}
|
handler, {"domain": domain, "multiple": True}
|
||||||
),
|
),
|
||||||
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector(
|
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector(
|
||||||
{"boolean": {}}
|
{"boolean": {}}
|
||||||
@ -36,18 +44,45 @@ def basic_group_options_schema(domain: str) -> vol.Schema:
|
|||||||
|
|
||||||
def basic_group_config_schema(domain: str) -> vol.Schema:
|
def basic_group_config_schema(domain: str) -> vol.Schema:
|
||||||
"""Generate config schema."""
|
"""Generate config schema."""
|
||||||
return vol.Schema({vol.Required("name"): selector.selector({"text": {}})}).extend(
|
return vol.Schema(
|
||||||
basic_group_options_schema(domain).schema
|
{
|
||||||
|
vol.Required("name"): selector.selector({"text": {}}),
|
||||||
|
vol.Required(CONF_ENTITIES): selector.selector(
|
||||||
|
{"entity": {"domain": domain, "multiple": True}}
|
||||||
|
),
|
||||||
|
vol.Required(CONF_HIDE_MEMBERS, default=False): selector.selector(
|
||||||
|
{"boolean": {}}
|
||||||
|
),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
BINARY_SENSOR_OPTIONS_SCHEMA = basic_group_options_schema("binary_sensor").extend(
|
def binary_sensor_options_schema(
|
||||||
|
handler: HelperConfigFlowHandler | HelperOptionsFlowHandler,
|
||||||
|
options: dict[str, Any],
|
||||||
|
) -> vol.Schema:
|
||||||
|
"""Generate options schema."""
|
||||||
|
return basic_group_options_schema("binary_sensor", handler, options).extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}),
|
vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
LIGHT_OPTIONS_SCHEMA = basic_group_options_schema("light").extend(
|
|
||||||
|
BINARY_SENSOR_CONFIG_SCHEMA = basic_group_config_schema("binary_sensor").extend(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_ALL, default=False): selector.selector({"boolean": {}}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def light_switch_options_schema(
|
||||||
|
domain: str,
|
||||||
|
handler: HelperConfigFlowHandler | HelperOptionsFlowHandler,
|
||||||
|
options: dict[str, Any],
|
||||||
|
) -> vol.Schema:
|
||||||
|
"""Generate options schema."""
|
||||||
|
return basic_group_options_schema(domain, handler, options).extend(
|
||||||
{
|
{
|
||||||
vol.Required(
|
vol.Required(
|
||||||
CONF_ALL, default=False, description={"advanced": True}
|
CONF_ALL, default=False, description={"advanced": True}
|
||||||
@ -55,17 +90,6 @@ LIGHT_OPTIONS_SCHEMA = basic_group_options_schema("light").extend(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SWITCH_OPTIONS_SCHEMA = basic_group_options_schema("switch").extend(
|
|
||||||
{
|
|
||||||
vol.Required(
|
|
||||||
CONF_ALL, default=False, description={"advanced": True}
|
|
||||||
): selector.selector({"boolean": {}}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
BINARY_SENSOR_CONFIG_SCHEMA = vol.Schema(
|
|
||||||
{vol.Required("name"): selector.selector({"text": {}})}
|
|
||||||
).extend(BINARY_SENSOR_OPTIONS_SCHEMA.schema)
|
|
||||||
|
|
||||||
GROUP_TYPES = [
|
GROUP_TYPES = [
|
||||||
"binary_sensor",
|
"binary_sensor",
|
||||||
@ -121,13 +145,15 @@ CONFIG_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = {
|
|||||||
|
|
||||||
OPTIONS_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = {
|
OPTIONS_FLOW: dict[str, HelperFlowFormStep | HelperFlowMenuStep] = {
|
||||||
"init": HelperFlowFormStep(None, next_step=choose_options_step),
|
"init": HelperFlowFormStep(None, next_step=choose_options_step),
|
||||||
"binary_sensor": HelperFlowFormStep(BINARY_SENSOR_OPTIONS_SCHEMA),
|
"binary_sensor": HelperFlowFormStep(binary_sensor_options_schema),
|
||||||
"cover": HelperFlowFormStep(basic_group_options_schema("cover")),
|
"cover": HelperFlowFormStep(partial(basic_group_options_schema, "cover")),
|
||||||
"fan": HelperFlowFormStep(basic_group_options_schema("fan")),
|
"fan": HelperFlowFormStep(partial(basic_group_options_schema, "fan")),
|
||||||
"light": HelperFlowFormStep(LIGHT_OPTIONS_SCHEMA),
|
"light": HelperFlowFormStep(partial(light_switch_options_schema, "light")),
|
||||||
"lock": HelperFlowFormStep(basic_group_options_schema("lock")),
|
"lock": HelperFlowFormStep(partial(basic_group_options_schema, "lock")),
|
||||||
"media_player": HelperFlowFormStep(basic_group_options_schema("media_player")),
|
"media_player": HelperFlowFormStep(
|
||||||
"switch": HelperFlowFormStep(SWITCH_OPTIONS_SCHEMA),
|
partial(basic_group_options_schema, "media_player")
|
||||||
|
),
|
||||||
|
"switch": HelperFlowFormStep(partial(light_switch_options_schema, "switch")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant import config_entries
|
|||||||
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
||||||
from homeassistant.data_entry_flow import FlowResult, UnknownHandler
|
from homeassistant.data_entry_flow import FlowResult, UnknownHandler
|
||||||
|
|
||||||
from . import entity_registry as er
|
from . import entity_registry as er, selector
|
||||||
|
|
||||||
|
|
||||||
class HelperFlowError(Exception):
|
class HelperFlowError(Exception):
|
||||||
@ -27,7 +27,10 @@ class HelperFlowFormStep:
|
|||||||
|
|
||||||
# Optional schema for requesting and validating user input. If schema validation
|
# Optional schema for requesting and validating user input. If schema validation
|
||||||
# fails, the step will be retried. If the schema is None, no user input is requested.
|
# fails, the step will be retried. If the schema is None, no user input is requested.
|
||||||
schema: vol.Schema | None
|
schema: vol.Schema | Callable[
|
||||||
|
[HelperConfigFlowHandler | HelperOptionsFlowHandler, dict[str, Any]],
|
||||||
|
vol.Schema | None,
|
||||||
|
] | None
|
||||||
|
|
||||||
# Optional function to validate user input.
|
# Optional function to validate user input.
|
||||||
# The validate_user_input function is called if the schema validates successfully.
|
# The validate_user_input function is called if the schema validates successfully.
|
||||||
@ -42,6 +45,20 @@ class HelperFlowFormStep:
|
|||||||
# If next_step returns None, the flow is ended with RESULT_TYPE_CREATE_ENTRY.
|
# If next_step returns None, the flow is ended with RESULT_TYPE_CREATE_ENTRY.
|
||||||
next_step: Callable[[dict[str, Any]], str | None] = lambda _: None
|
next_step: Callable[[dict[str, Any]], str | None] = lambda _: None
|
||||||
|
|
||||||
|
# Optional function to allow amending a form schema.
|
||||||
|
# The update_form_schema function is called before async_show_form is called. The
|
||||||
|
# update_form_schema function is passed the handler, which is either an instance of
|
||||||
|
# HelperConfigFlowHandler or HelperOptionsFlowHandler, the schema, and the union of
|
||||||
|
# config entry options and user input from previous steps.
|
||||||
|
update_form_schema: Callable[
|
||||||
|
[
|
||||||
|
HelperConfigFlowHandler | HelperOptionsFlowHandler,
|
||||||
|
vol.Schema,
|
||||||
|
dict[str, Any],
|
||||||
|
],
|
||||||
|
vol.Schema,
|
||||||
|
] = lambda _handler, schema, _options: schema
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class HelperFlowMenuStep:
|
class HelperFlowMenuStep:
|
||||||
@ -73,6 +90,15 @@ class HelperCommonFlowHandler:
|
|||||||
return await self._async_form_step(step_id, user_input)
|
return await self._async_form_step(step_id, user_input)
|
||||||
return await self._async_menu_step(step_id, user_input)
|
return await self._async_menu_step(step_id, user_input)
|
||||||
|
|
||||||
|
def _get_schema(
|
||||||
|
self, form_step: HelperFlowFormStep, options: dict[str, Any]
|
||||||
|
) -> vol.Schema | None:
|
||||||
|
if form_step.schema is None:
|
||||||
|
return None
|
||||||
|
if isinstance(form_step.schema, vol.Schema):
|
||||||
|
return form_step.schema
|
||||||
|
return form_step.schema(self._handler, options)
|
||||||
|
|
||||||
async def _async_form_step(
|
async def _async_form_step(
|
||||||
self, step_id: str, user_input: dict[str, Any] | None = None
|
self, step_id: str, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
@ -81,7 +107,7 @@ class HelperCommonFlowHandler:
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
user_input is not None
|
user_input is not None
|
||||||
and (data_schema := form_step.schema)
|
and (data_schema := self._get_schema(form_step, self._options))
|
||||||
and data_schema.schema
|
and data_schema.schema
|
||||||
and not self._handler.show_advanced_options
|
and not self._handler.show_advanced_options
|
||||||
):
|
):
|
||||||
@ -133,7 +159,10 @@ class HelperCommonFlowHandler:
|
|||||||
options = dict(self._options)
|
options = dict(self._options)
|
||||||
if user_input:
|
if user_input:
|
||||||
options.update(user_input)
|
options.update(user_input)
|
||||||
if (data_schema := form_step.schema) and data_schema.schema:
|
|
||||||
|
if (
|
||||||
|
data_schema := self._get_schema(form_step, self._options)
|
||||||
|
) and data_schema.schema:
|
||||||
# Make a copy of the schema with suggested values set to saved options
|
# Make a copy of the schema with suggested values set to saved options
|
||||||
schema = {}
|
schema = {}
|
||||||
for key, val in data_schema.schema.items():
|
for key, val in data_schema.schema.items():
|
||||||
@ -282,7 +311,7 @@ class HelperOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize options flow."""
|
"""Initialize options flow."""
|
||||||
self._common_handler = HelperCommonFlowHandler(self, options_flow, config_entry)
|
self._common_handler = HelperCommonFlowHandler(self, options_flow, config_entry)
|
||||||
self._config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
self._async_options_flow_finished = async_options_flow_finished
|
self._async_options_flow_finished = async_options_flow_finished
|
||||||
|
|
||||||
for step in options_flow:
|
for step in options_flow:
|
||||||
@ -337,3 +366,21 @@ def wrapped_entity_config_entry_title(
|
|||||||
if state:
|
if state:
|
||||||
return state.name or object_id
|
return state.name or object_id
|
||||||
return object_id
|
return object_id
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def entity_selector_without_own_entities(
|
||||||
|
handler: HelperOptionsFlowHandler,
|
||||||
|
entity_selector_config: dict[str, Any],
|
||||||
|
) -> vol.Schema:
|
||||||
|
"""Return an entity selector which excludes own entities."""
|
||||||
|
entity_registry = er.async_get(handler.hass)
|
||||||
|
entities = er.async_entries_for_config_entry(
|
||||||
|
entity_registry,
|
||||||
|
handler.config_entry.entry_id, # pylint: disable=protected-access
|
||||||
|
)
|
||||||
|
entity_ids = [ent.entity_id for ent in entities]
|
||||||
|
|
||||||
|
return selector.selector(
|
||||||
|
{"entity": {**entity_selector_config, "exclude_entities": entity_ids}}
|
||||||
|
)
|
||||||
|
@ -224,6 +224,9 @@ async def test_options(
|
|||||||
assert result["step_id"] == group_type
|
assert result["step_id"] == group_type
|
||||||
assert get_suggested(result["data_schema"].schema, "entities") == members1
|
assert get_suggested(result["data_schema"].schema, "entities") == members1
|
||||||
assert "name" not in result["data_schema"].schema
|
assert "name" not in result["data_schema"].schema
|
||||||
|
assert result["data_schema"].schema["entities"].config["exclude_entities"] == [
|
||||||
|
f"{group_type}.bed_room"
|
||||||
|
]
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
result = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user