2022-03-30 21:36:47 +00:00
|
|
|
"""Helpers for creating schema based data entry flows."""
|
2022-03-04 19:02:17 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from abc import abstractmethod
|
2022-03-09 12:18:19 +00:00
|
|
|
from collections.abc import Callable, Mapping
|
2022-03-07 12:05:04 +00:00
|
|
|
import copy
|
2022-03-09 12:18:19 +00:00
|
|
|
from dataclasses import dataclass
|
2022-03-23 15:34:44 +00:00
|
|
|
import types
|
|
|
|
from typing import Any, cast
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant import config_entries
|
2022-03-10 15:18:26 +00:00
|
|
|
from homeassistant.core import HomeAssistant, callback, split_entity_id
|
2022-03-09 12:18:19 +00:00
|
|
|
from homeassistant.data_entry_flow import FlowResult, UnknownHandler
|
|
|
|
|
2022-03-30 16:07:47 +00:00
|
|
|
from . import entity_registry as er, selector
|
2022-03-10 15:18:26 +00:00
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-03-30 21:36:47 +00:00
|
|
|
class SchemaFlowError(Exception):
|
2022-03-22 10:32:19 +00:00
|
|
|
"""Validation failed."""
|
|
|
|
|
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
@dataclass
|
2022-11-24 20:59:41 +00:00
|
|
|
class SchemaFlowStep:
|
2022-03-30 21:36:47 +00:00
|
|
|
"""Define a config or options flow step."""
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-11-24 20:59:41 +00:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class SchemaFlowFormStep(SchemaFlowStep):
|
|
|
|
"""Define a config or options flow form step."""
|
|
|
|
|
2022-03-30 16:07:47 +00:00
|
|
|
schema: vol.Schema | Callable[
|
2022-11-25 09:50:38 +00:00
|
|
|
[SchemaCommonFlowHandler], vol.Schema | None
|
|
|
|
] | None = None
|
2022-11-24 20:59:41 +00:00
|
|
|
"""Optional voluptuous schema, or function which returns a schema or None, for
|
|
|
|
requesting and validating user input.
|
|
|
|
|
2022-11-25 09:50:38 +00:00
|
|
|
- If a function is specified, the function will be passed the current
|
|
|
|
`SchemaCommonFlowHandler`.
|
2022-11-24 20:59:41 +00:00
|
|
|
- If schema validation fails, the step will be retried. If the schema is None, no
|
|
|
|
user input is requested.
|
|
|
|
"""
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-11-25 08:29:54 +00:00
|
|
|
validate_user_input: Callable[
|
|
|
|
[SchemaCommonFlowHandler, dict[str, Any]], dict[str, Any]
|
|
|
|
] | None = None
|
2022-11-24 17:55:53 +00:00
|
|
|
"""Optional function to validate user input.
|
2022-03-22 10:32:19 +00:00
|
|
|
|
2022-11-24 17:55:53 +00:00
|
|
|
- The `validate_user_input` function is called if the schema validates successfully.
|
2022-11-25 09:50:38 +00:00
|
|
|
- The first argument is a reference to the current `SchemaCommonFlowHandler`.
|
|
|
|
- The second argument is the user input from the current step.
|
2022-11-24 17:55:53 +00:00
|
|
|
- The `validate_user_input` should raise `SchemaFlowError` is user input is invalid.
|
|
|
|
"""
|
|
|
|
|
|
|
|
next_step: Callable[[dict[str, Any]], str] | str | None = None
|
|
|
|
"""Optional property to identify next step.
|
|
|
|
|
|
|
|
- If `next_step` is a function, it is called if the schema validates successfully or
|
|
|
|
if no schema is defined. The `next_step` function is passed the union of config entry
|
|
|
|
options and user input from previous steps.
|
|
|
|
- If `next_step` is None, the flow is ended with `FlowResultType.CREATE_ENTRY`.
|
|
|
|
"""
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
@dataclass
|
2022-11-24 20:59:41 +00:00
|
|
|
class SchemaFlowMenuStep(SchemaFlowStep):
|
2022-03-30 21:36:47 +00:00
|
|
|
"""Define a config or options flow menu step."""
|
2022-03-23 15:34:44 +00:00
|
|
|
|
|
|
|
# Menu options
|
|
|
|
options: list[str] | dict[str, str]
|
|
|
|
|
|
|
|
|
2022-03-30 21:36:47 +00:00
|
|
|
class SchemaCommonFlowHandler:
|
|
|
|
"""Handle a schema based config or options flow."""
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
2022-03-30 21:36:47 +00:00
|
|
|
handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler,
|
2022-11-24 20:59:41 +00:00
|
|
|
flow: Mapping[str, SchemaFlowStep],
|
2022-11-24 11:18:09 +00:00
|
|
|
options: dict[str, Any] | None,
|
2022-03-04 19:02:17 +00:00
|
|
|
) -> None:
|
|
|
|
"""Initialize a common handler."""
|
2022-03-09 12:18:19 +00:00
|
|
|
self._flow = flow
|
2022-03-04 19:02:17 +00:00
|
|
|
self._handler = handler
|
2022-11-24 11:18:09 +00:00
|
|
|
self._options = options if options is not None else {}
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-11-25 09:50:38 +00:00
|
|
|
@property
|
|
|
|
def parent_handler(self) -> SchemaConfigFlowHandler | SchemaOptionsFlowHandler:
|
|
|
|
"""Return parent handler."""
|
|
|
|
return self._handler
|
|
|
|
|
2022-03-07 12:05:04 +00:00
|
|
|
async def async_step(
|
2022-03-09 12:18:19 +00:00
|
|
|
self, step_id: str, user_input: dict[str, Any] | None = None
|
2022-03-07 12:05:04 +00:00
|
|
|
) -> FlowResult:
|
2022-03-04 19:02:17 +00:00
|
|
|
"""Handle a step."""
|
2022-03-30 21:36:47 +00:00
|
|
|
if isinstance(self._flow[step_id], SchemaFlowFormStep):
|
2022-03-23 15:34:44 +00:00
|
|
|
return await self._async_form_step(step_id, user_input)
|
|
|
|
return await self._async_menu_step(step_id, user_input)
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-11-25 09:50:38 +00:00
|
|
|
def _get_schema(self, form_step: SchemaFlowFormStep) -> vol.Schema | None:
|
2022-03-30 16:07:47 +00:00
|
|
|
if form_step.schema is None:
|
|
|
|
return None
|
|
|
|
if isinstance(form_step.schema, vol.Schema):
|
|
|
|
return form_step.schema
|
2022-11-25 09:50:38 +00:00
|
|
|
return form_step.schema(self)
|
2022-03-30 16:07:47 +00:00
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
async def _async_form_step(
|
|
|
|
self, step_id: str, user_input: dict[str, Any] | None = None
|
|
|
|
) -> FlowResult:
|
|
|
|
"""Handle a form step."""
|
2022-03-30 21:36:47 +00:00
|
|
|
form_step: SchemaFlowFormStep = cast(SchemaFlowFormStep, self._flow[step_id])
|
2022-03-23 15:34:44 +00:00
|
|
|
|
2022-03-24 15:51:31 +00:00
|
|
|
if (
|
|
|
|
user_input is not None
|
2022-11-25 09:50:38 +00:00
|
|
|
and (data_schema := self._get_schema(form_step))
|
2022-03-24 15:51:31 +00:00
|
|
|
and data_schema.schema
|
|
|
|
and not self._handler.show_advanced_options
|
|
|
|
):
|
|
|
|
# Add advanced field default if not set
|
|
|
|
for key in data_schema.schema.keys():
|
|
|
|
if isinstance(key, (vol.Optional, vol.Required)):
|
|
|
|
if (
|
|
|
|
key.description
|
|
|
|
and key.description.get("advanced")
|
|
|
|
and key.default is not vol.UNDEFINED
|
|
|
|
and key not in self._options
|
|
|
|
):
|
|
|
|
user_input[str(key.schema)] = key.default()
|
|
|
|
|
2022-11-25 08:29:54 +00:00
|
|
|
if user_input is not None and form_step.validate_user_input is not None:
|
2022-03-22 10:32:19 +00:00
|
|
|
# Do extra validation of user input
|
|
|
|
try:
|
2022-11-25 08:29:54 +00:00
|
|
|
user_input = form_step.validate_user_input(self, user_input)
|
2022-03-30 21:36:47 +00:00
|
|
|
except SchemaFlowError as exc:
|
2022-03-23 15:34:44 +00:00
|
|
|
return self._show_next_step(step_id, exc, user_input)
|
2022-03-22 10:32:19 +00:00
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
if user_input is not None:
|
|
|
|
# User input was validated successfully, update options
|
|
|
|
self._options.update(user_input)
|
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
next_step_id: str = step_id
|
2022-09-26 02:01:27 +00:00
|
|
|
if user_input is not None or form_step.schema is None:
|
2022-03-09 12:18:19 +00:00
|
|
|
# Get next step
|
2022-11-24 13:37:55 +00:00
|
|
|
if form_step.next_step is None:
|
2022-03-09 12:18:19 +00:00
|
|
|
# Flow done, create entry or update config entry options
|
|
|
|
return self._handler.async_create_entry(data=self._options)
|
|
|
|
|
2022-11-24 17:55:53 +00:00
|
|
|
if isinstance(form_step.next_step, str):
|
|
|
|
next_step_id = form_step.next_step
|
|
|
|
else:
|
|
|
|
next_step_id = form_step.next_step(self._options)
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-03-22 10:32:19 +00:00
|
|
|
return self._show_next_step(next_step_id)
|
|
|
|
|
|
|
|
def _show_next_step(
|
|
|
|
self,
|
|
|
|
next_step_id: str,
|
2022-03-30 21:36:47 +00:00
|
|
|
error: SchemaFlowError | None = None,
|
2022-03-22 10:32:19 +00:00
|
|
|
user_input: dict[str, Any] | None = None,
|
|
|
|
) -> FlowResult:
|
2022-03-23 15:34:44 +00:00
|
|
|
"""Show form for next step."""
|
2022-03-22 10:32:19 +00:00
|
|
|
options = dict(self._options)
|
|
|
|
if user_input:
|
|
|
|
options.update(user_input)
|
2022-03-30 16:07:47 +00:00
|
|
|
|
2022-11-24 16:30:01 +00:00
|
|
|
if isinstance(self._flow[next_step_id], SchemaFlowMenuStep):
|
|
|
|
menu_step = cast(SchemaFlowMenuStep, self._flow[next_step_id])
|
|
|
|
return self._handler.async_show_menu(
|
|
|
|
step_id=next_step_id,
|
|
|
|
menu_options=menu_step.options,
|
|
|
|
)
|
|
|
|
|
|
|
|
form_step = cast(SchemaFlowFormStep, self._flow[next_step_id])
|
|
|
|
|
2022-11-25 09:50:38 +00:00
|
|
|
if (data_schema := self._get_schema(form_step)) and data_schema.schema:
|
2022-03-22 10:32:19 +00:00
|
|
|
# Make a copy of the schema with suggested values set to saved options
|
|
|
|
schema = {}
|
|
|
|
for key, val in data_schema.schema.items():
|
2022-03-24 15:51:31 +00:00
|
|
|
|
|
|
|
if isinstance(key, vol.Marker):
|
|
|
|
# Exclude advanced field
|
|
|
|
if (
|
|
|
|
key.description
|
|
|
|
and key.description.get("advanced")
|
|
|
|
and not self._handler.show_advanced_options
|
|
|
|
):
|
|
|
|
continue
|
|
|
|
|
2022-03-22 10:32:19 +00:00
|
|
|
new_key = key
|
|
|
|
if key in options and isinstance(key, vol.Marker):
|
2022-03-09 12:18:19 +00:00
|
|
|
# Copy the marker to not modify the flow schema
|
|
|
|
new_key = copy.copy(key)
|
2022-03-22 10:32:19 +00:00
|
|
|
new_key.description = {"suggested_value": options[key]}
|
|
|
|
schema[new_key] = val
|
2022-03-09 12:18:19 +00:00
|
|
|
data_schema = vol.Schema(schema)
|
|
|
|
|
2022-03-22 10:32:19 +00:00
|
|
|
errors = {"base": str(error)} if error else None
|
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
# Show form for next step
|
2022-03-04 19:02:17 +00:00
|
|
|
return self._handler.async_show_form(
|
2022-11-24 13:37:55 +00:00
|
|
|
step_id=next_step_id,
|
|
|
|
data_schema=data_schema,
|
|
|
|
errors=errors,
|
|
|
|
last_step=form_step.next_step is None,
|
2022-03-04 19:02:17 +00:00
|
|
|
)
|
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
async def _async_menu_step(
|
|
|
|
self, step_id: str, user_input: dict[str, Any] | None = None
|
|
|
|
) -> FlowResult:
|
|
|
|
"""Handle a menu step."""
|
2022-11-24 16:30:01 +00:00
|
|
|
menu_step: SchemaFlowMenuStep = cast(SchemaFlowMenuStep, self._flow[step_id])
|
2022-03-23 15:34:44 +00:00
|
|
|
return self._handler.async_show_menu(
|
|
|
|
step_id=step_id,
|
2022-11-24 16:30:01 +00:00
|
|
|
menu_options=menu_step.options,
|
2022-03-23 15:34:44 +00:00
|
|
|
)
|
|
|
|
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-03-30 21:36:47 +00:00
|
|
|
class SchemaConfigFlowHandler(config_entries.ConfigFlow):
|
|
|
|
"""Handle a schema based config flow."""
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-11-24 20:59:41 +00:00
|
|
|
config_flow: Mapping[str, SchemaFlowStep]
|
|
|
|
options_flow: Mapping[str, SchemaFlowStep] | None = None
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
VERSION = 1
|
|
|
|
|
|
|
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
2022-03-09 12:18:19 +00:00
|
|
|
"""Initialize a subclass."""
|
2022-03-04 19:02:17 +00:00
|
|
|
super().__init_subclass__(**kwargs)
|
|
|
|
|
2022-03-07 12:05:04 +00:00
|
|
|
@callback
|
|
|
|
def _async_get_options_flow(
|
|
|
|
config_entry: config_entries.ConfigEntry,
|
|
|
|
) -> config_entries.OptionsFlow:
|
|
|
|
"""Get the options flow for this handler."""
|
2022-03-09 12:18:19 +00:00
|
|
|
if cls.options_flow is None:
|
2022-03-07 12:05:04 +00:00
|
|
|
raise UnknownHandler
|
|
|
|
|
2022-03-30 21:36:47 +00:00
|
|
|
return SchemaOptionsFlowHandler(
|
2022-03-22 11:14:34 +00:00
|
|
|
config_entry, cls.options_flow, cls.async_options_flow_finished
|
|
|
|
)
|
2022-03-07 12:05:04 +00:00
|
|
|
|
|
|
|
# Create an async_get_options_flow method
|
|
|
|
cls.async_get_options_flow = _async_get_options_flow # type: ignore[assignment]
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-03-07 12:05:04 +00:00
|
|
|
# Create flow step methods for each step defined in the flow schema
|
2022-03-09 12:18:19 +00:00
|
|
|
for step in cls.config_flow:
|
2022-03-23 15:34:44 +00:00
|
|
|
setattr(cls, f"async_step_{step}", cls._async_step(step))
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
def __init__(self) -> None:
|
|
|
|
"""Initialize config flow."""
|
2022-03-30 21:36:47 +00:00
|
|
|
self._common_handler = SchemaCommonFlowHandler(self, self.config_flow, None)
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-03-07 12:05:04 +00:00
|
|
|
@classmethod
|
|
|
|
@callback
|
|
|
|
def async_supports_options_flow(
|
|
|
|
cls, config_entry: config_entries.ConfigEntry
|
|
|
|
) -> bool:
|
|
|
|
"""Return options flow support for this handler."""
|
2022-03-09 12:18:19 +00:00
|
|
|
return cls.options_flow is not None
|
2022-03-07 12:05:04 +00:00
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
@staticmethod
|
|
|
|
def _async_step(step_id: str) -> Callable:
|
|
|
|
"""Generate a step handler."""
|
|
|
|
|
|
|
|
async def _async_step(
|
2022-03-30 21:36:47 +00:00
|
|
|
self: SchemaConfigFlowHandler, user_input: dict[str, Any] | None = None
|
2022-03-23 15:34:44 +00:00
|
|
|
) -> FlowResult:
|
|
|
|
"""Handle a config flow step."""
|
|
|
|
# pylint: disable-next=protected-access
|
|
|
|
result = await self._common_handler.async_step(step_id, user_input)
|
|
|
|
return result
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
return _async_step
|
2022-03-04 19:02:17 +00:00
|
|
|
|
|
|
|
@abstractmethod
|
2022-03-22 11:14:34 +00:00
|
|
|
@callback
|
2022-03-09 12:18:19 +00:00
|
|
|
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
|
|
|
|
"""Return config entry title.
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
The options parameter contains config entry options, which is the union of user
|
|
|
|
input from the config flow steps.
|
|
|
|
"""
|
2022-03-04 19:02:17 +00:00
|
|
|
|
2022-03-22 11:14:34 +00:00
|
|
|
@callback
|
|
|
|
def async_config_flow_finished(self, options: Mapping[str, Any]) -> None:
|
|
|
|
"""Take necessary actions after the config flow is finished, if needed.
|
|
|
|
|
|
|
|
The options parameter contains config entry options, which is the union of user
|
|
|
|
input from the config flow steps.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@callback
|
|
|
|
@staticmethod
|
|
|
|
def async_options_flow_finished(
|
|
|
|
hass: HomeAssistant, options: Mapping[str, Any]
|
|
|
|
) -> None:
|
|
|
|
"""Take necessary actions after the options flow is finished, if needed.
|
|
|
|
|
|
|
|
The options parameter contains config entry options, which is the union of stored
|
|
|
|
options and user input from the options flow steps.
|
|
|
|
"""
|
|
|
|
|
2022-03-07 12:05:04 +00:00
|
|
|
@callback
|
2022-03-09 12:18:19 +00:00
|
|
|
def async_create_entry( # pylint: disable=arguments-differ
|
|
|
|
self,
|
|
|
|
data: Mapping[str, Any],
|
|
|
|
**kwargs: Any,
|
|
|
|
) -> FlowResult:
|
|
|
|
"""Finish config flow and create a config entry."""
|
2022-03-22 11:14:34 +00:00
|
|
|
self.async_config_flow_finished(data)
|
2022-03-09 12:18:19 +00:00
|
|
|
return super().async_create_entry(
|
|
|
|
data={}, options=data, title=self.async_config_entry_title(data), **kwargs
|
|
|
|
)
|
2022-03-07 12:05:04 +00:00
|
|
|
|
|
|
|
|
2022-11-24 11:18:09 +00:00
|
|
|
class SchemaOptionsFlowHandler(config_entries.OptionsFlowWithConfigEntry):
|
2022-03-30 21:36:47 +00:00
|
|
|
"""Handle a schema based options flow."""
|
2022-03-07 12:05:04 +00:00
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
config_entry: config_entries.ConfigEntry,
|
2022-11-24 20:59:41 +00:00
|
|
|
options_flow: Mapping[str, SchemaFlowStep],
|
2022-11-24 13:43:40 +00:00
|
|
|
async_options_flow_finished: Callable[[HomeAssistant, Mapping[str, Any]], None]
|
|
|
|
| None = None,
|
2022-03-07 12:05:04 +00:00
|
|
|
) -> None:
|
2022-11-24 13:43:40 +00:00
|
|
|
"""Initialize options flow.
|
|
|
|
|
|
|
|
If needed, `async_options_flow_finished` can be set to take necessary actions
|
|
|
|
after the options flow is finished. The second parameter contains config entry
|
|
|
|
options, which is the union of stored options and user input from the options
|
|
|
|
flow steps.
|
|
|
|
"""
|
2022-11-24 11:18:09 +00:00
|
|
|
super().__init__(config_entry)
|
|
|
|
self._common_handler = SchemaCommonFlowHandler(
|
|
|
|
self, options_flow, self._options
|
|
|
|
)
|
2022-03-22 11:14:34 +00:00
|
|
|
self._async_options_flow_finished = async_options_flow_finished
|
2022-03-07 12:05:04 +00:00
|
|
|
|
2022-03-09 12:18:19 +00:00
|
|
|
for step in options_flow:
|
2022-03-23 15:34:44 +00:00
|
|
|
setattr(
|
|
|
|
self,
|
|
|
|
f"async_step_{step}",
|
|
|
|
types.MethodType(self._async_step(step), self),
|
|
|
|
)
|
2022-03-09 12:18:19 +00:00
|
|
|
|
2022-03-23 15:34:44 +00:00
|
|
|
@staticmethod
|
|
|
|
def _async_step(step_id: str) -> Callable:
|
|
|
|
"""Generate a step handler."""
|
|
|
|
|
|
|
|
async def _async_step(
|
2022-03-30 21:36:47 +00:00
|
|
|
self: SchemaConfigFlowHandler, user_input: dict[str, Any] | None = None
|
2022-03-23 15:34:44 +00:00
|
|
|
) -> FlowResult:
|
|
|
|
"""Handle an options flow step."""
|
|
|
|
# pylint: disable-next=protected-access
|
|
|
|
result = await self._common_handler.async_step(step_id, user_input)
|
|
|
|
return result
|
|
|
|
|
|
|
|
return _async_step
|
2022-03-09 12:18:19 +00:00
|
|
|
|
|
|
|
@callback
|
|
|
|
def async_create_entry( # pylint: disable=arguments-differ
|
|
|
|
self,
|
2022-03-22 11:14:34 +00:00
|
|
|
data: Mapping[str, Any],
|
2022-03-09 12:18:19 +00:00
|
|
|
**kwargs: Any,
|
|
|
|
) -> FlowResult:
|
|
|
|
"""Finish config flow and create a config entry."""
|
2022-11-24 13:43:40 +00:00
|
|
|
if self._async_options_flow_finished:
|
|
|
|
self._async_options_flow_finished(self.hass, data)
|
2022-03-22 11:14:34 +00:00
|
|
|
return super().async_create_entry(title="", data=data, **kwargs)
|
2022-03-10 15:18:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
|
|
|
def wrapped_entity_config_entry_title(
|
|
|
|
hass: HomeAssistant, entity_id_or_uuid: str
|
|
|
|
) -> str:
|
|
|
|
"""Generate title for a config entry wrapping a single entity.
|
|
|
|
|
|
|
|
If the entity is registered, use the registry entry's name.
|
|
|
|
If the entity is in the state machine, use the name from the state.
|
|
|
|
Otherwise, fall back to the object ID.
|
|
|
|
"""
|
|
|
|
registry = er.async_get(hass)
|
|
|
|
entity_id = er.async_validate_entity_id(registry, entity_id_or_uuid)
|
|
|
|
object_id = split_entity_id(entity_id)[1]
|
|
|
|
entry = registry.async_get(entity_id)
|
|
|
|
if entry:
|
|
|
|
return entry.name or entry.original_name or object_id
|
|
|
|
state = hass.states.get(entity_id)
|
|
|
|
if state:
|
|
|
|
return state.name or object_id
|
|
|
|
return object_id
|
2022-03-30 16:07:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
|
|
|
def entity_selector_without_own_entities(
|
2022-03-30 21:36:47 +00:00
|
|
|
handler: SchemaOptionsFlowHandler,
|
2022-04-11 07:20:56 +00:00
|
|
|
entity_selector_config: selector.EntitySelectorConfig,
|
2022-03-30 16:07:47 +00:00
|
|
|
) -> 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,
|
2022-04-13 16:36:05 +00:00
|
|
|
handler.config_entry.entry_id,
|
2022-03-30 16:07:47 +00:00
|
|
|
)
|
|
|
|
entity_ids = [ent.entity_id for ent in entities]
|
|
|
|
|
2022-04-11 07:20:56 +00:00
|
|
|
final_selector_config = entity_selector_config.copy()
|
|
|
|
final_selector_config["exclude_entities"] = entity_ids
|
|
|
|
|
|
|
|
return selector.EntitySelector(final_selector_config)
|