mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 11:47:06 +00:00
Support readonly selectors in config_flows (#129456)
* Allow disabled selectors in config flows. Show hidden options for history_stats. * fix tests * use optional instead of required * rename flag to readonly * rename to read_only * Update to use read_only field as part of selector definition * lint fix * Fix test * All selectors
This commit is contained in:
parent
83ec45e4fc
commit
7af731694f
@ -18,6 +18,7 @@ from homeassistant.helpers.selector import (
|
||||
DurationSelector,
|
||||
DurationSelectorConfig,
|
||||
EntitySelector,
|
||||
EntitySelectorConfig,
|
||||
SelectSelector,
|
||||
SelectSelectorConfig,
|
||||
SelectSelectorMode,
|
||||
@ -66,6 +67,20 @@ DATA_SCHEMA_SETUP = vol.Schema(
|
||||
)
|
||||
DATA_SCHEMA_OPTIONS = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_ENTITY_ID): EntitySelector(
|
||||
EntitySelectorConfig(read_only=True)
|
||||
),
|
||||
vol.Optional(CONF_STATE): TextSelector(
|
||||
TextSelectorConfig(multiple=True, read_only=True)
|
||||
),
|
||||
vol.Optional(CONF_TYPE): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=CONF_TYPE_KEYS,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
translation_key=CONF_TYPE,
|
||||
read_only=True,
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_START): TemplateSelector(),
|
||||
vol.Optional(CONF_END): TemplateSelector(),
|
||||
vol.Optional(CONF_DURATION): DurationSelector(
|
||||
|
@ -26,11 +26,17 @@
|
||||
"options": {
|
||||
"description": "Read the documentation for further details on how to configure the history stats sensor using these options.",
|
||||
"data": {
|
||||
"entity_id": "[%key:component::history_stats::config::step::user::data::entity_id%]",
|
||||
"state": "[%key:component::history_stats::config::step::user::data::state%]",
|
||||
"type": "[%key:component::history_stats::config::step::user::data::type%]",
|
||||
"start": "Start",
|
||||
"end": "End",
|
||||
"duration": "Duration"
|
||||
},
|
||||
"data_description": {
|
||||
"entity_id": "[%key:component::history_stats::config::step::user::data_description::entity_id%]",
|
||||
"state": "[%key:component::history_stats::config::step::user::data_description::state%]",
|
||||
"type": "[%key:component::history_stats::config::step::user::data_description::type%]",
|
||||
"start": "When to start the measure (timestamp or datetime). Can be a template.",
|
||||
"end": "When to stop the measure (timestamp or datetime). Can be a template",
|
||||
"duration": "Duration of the measure."
|
||||
@ -49,11 +55,17 @@
|
||||
"init": {
|
||||
"description": "[%key:component::history_stats::config::step::options::description%]",
|
||||
"data": {
|
||||
"entity_id": "[%key:component::history_stats::config::step::user::data::entity_id%]",
|
||||
"state": "[%key:component::history_stats::config::step::user::data::state%]",
|
||||
"type": "[%key:component::history_stats::config::step::user::data::type%]",
|
||||
"start": "[%key:component::history_stats::config::step::options::data::start%]",
|
||||
"end": "[%key:component::history_stats::config::step::options::data::end%]",
|
||||
"duration": "[%key:component::history_stats::config::step::options::data::duration%]"
|
||||
},
|
||||
"data_description": {
|
||||
"entity_id": "[%key:component::history_stats::config::step::user::data_description::entity_id%]",
|
||||
"state": "[%key:component::history_stats::config::step::user::data_description::state%]",
|
||||
"type": "[%key:component::history_stats::config::step::user::data_description::type%]",
|
||||
"start": "[%key:component::history_stats::config::step::options::data_description::start%]",
|
||||
"end": "[%key:component::history_stats::config::step::options::data_description::end%]",
|
||||
"duration": "[%key:component::history_stats::config::step::options::data_description::duration%]"
|
||||
|
@ -214,6 +214,11 @@ class SchemaCommonFlowHandler:
|
||||
and key.description.get("advanced")
|
||||
and not self._handler.show_advanced_options
|
||||
)
|
||||
and not (
|
||||
# don't remove read_only keys
|
||||
isinstance(data_schema.schema[key], selector.Selector)
|
||||
and data_schema.schema[key].config.get("read_only")
|
||||
)
|
||||
):
|
||||
# Key not present, delete keys old value (if present) too
|
||||
values.pop(key.schema, None)
|
||||
|
@ -131,6 +131,19 @@ def _validate_supported_features(supported_features: int | list[str]) -> int:
|
||||
return feature_mask
|
||||
|
||||
|
||||
BASE_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional("read_only"): bool,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BaseSelectorConfig(TypedDict, total=False):
|
||||
"""Class to common options of all selectors."""
|
||||
|
||||
read_only: bool
|
||||
|
||||
|
||||
ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
# Integration that provided the entity
|
||||
@ -183,7 +196,7 @@ class DeviceFilterSelectorConfig(TypedDict, total=False):
|
||||
model_id: str
|
||||
|
||||
|
||||
class ActionSelectorConfig(TypedDict):
|
||||
class ActionSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent an action selector config."""
|
||||
|
||||
|
||||
@ -193,7 +206,7 @@ class ActionSelector(Selector[ActionSelectorConfig]):
|
||||
|
||||
selector_type = "action"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: ActionSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -204,7 +217,7 @@ class ActionSelector(Selector[ActionSelectorConfig]):
|
||||
return data
|
||||
|
||||
|
||||
class AddonSelectorConfig(TypedDict, total=False):
|
||||
class AddonSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an addon selector config."""
|
||||
|
||||
name: str
|
||||
@ -217,7 +230,7 @@ class AddonSelector(Selector[AddonSelectorConfig]):
|
||||
|
||||
selector_type = "addon"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("name"): str,
|
||||
vol.Optional("slug"): str,
|
||||
@ -234,7 +247,7 @@ class AddonSelector(Selector[AddonSelectorConfig]):
|
||||
return addon
|
||||
|
||||
|
||||
class AreaSelectorConfig(TypedDict, total=False):
|
||||
class AreaSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an area selector config."""
|
||||
|
||||
entity: EntityFilterSelectorConfig | list[EntityFilterSelectorConfig]
|
||||
@ -248,7 +261,7 @@ class AreaSelector(Selector[AreaSelectorConfig]):
|
||||
|
||||
selector_type = "area"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("entity"): vol.All(
|
||||
cv.ensure_list,
|
||||
@ -276,7 +289,7 @@ class AreaSelector(Selector[AreaSelectorConfig]):
|
||||
return [vol.Schema(str)(val) for val in data]
|
||||
|
||||
|
||||
class AssistPipelineSelectorConfig(TypedDict, total=False):
|
||||
class AssistPipelineSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an assist pipeline selector config."""
|
||||
|
||||
|
||||
@ -286,7 +299,7 @@ class AssistPipelineSelector(Selector[AssistPipelineSelectorConfig]):
|
||||
|
||||
selector_type = "assist_pipeline"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: AssistPipelineSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -298,7 +311,7 @@ class AssistPipelineSelector(Selector[AssistPipelineSelectorConfig]):
|
||||
return pipeline
|
||||
|
||||
|
||||
class AttributeSelectorConfig(TypedDict, total=False):
|
||||
class AttributeSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an attribute selector config."""
|
||||
|
||||
entity_id: Required[str]
|
||||
@ -311,7 +324,7 @@ class AttributeSelector(Selector[AttributeSelectorConfig]):
|
||||
|
||||
selector_type = "attribute"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Required("entity_id"): cv.entity_id,
|
||||
# hide_attributes is used to hide attributes in the frontend.
|
||||
@ -330,7 +343,7 @@ class AttributeSelector(Selector[AttributeSelectorConfig]):
|
||||
return attribute
|
||||
|
||||
|
||||
class BackupLocationSelectorConfig(TypedDict, total=False):
|
||||
class BackupLocationSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a backup location selector config."""
|
||||
|
||||
|
||||
@ -340,7 +353,7 @@ class BackupLocationSelector(Selector[BackupLocationSelectorConfig]):
|
||||
|
||||
selector_type = "backup_location"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: BackupLocationSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -352,7 +365,7 @@ class BackupLocationSelector(Selector[BackupLocationSelectorConfig]):
|
||||
return name
|
||||
|
||||
|
||||
class BooleanSelectorConfig(TypedDict):
|
||||
class BooleanSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a boolean selector config."""
|
||||
|
||||
|
||||
@ -362,7 +375,7 @@ class BooleanSelector(Selector[BooleanSelectorConfig]):
|
||||
|
||||
selector_type = "boolean"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: BooleanSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -374,7 +387,7 @@ class BooleanSelector(Selector[BooleanSelectorConfig]):
|
||||
return value
|
||||
|
||||
|
||||
class ColorRGBSelectorConfig(TypedDict):
|
||||
class ColorRGBSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a color RGB selector config."""
|
||||
|
||||
|
||||
@ -384,7 +397,7 @@ class ColorRGBSelector(Selector[ColorRGBSelectorConfig]):
|
||||
|
||||
selector_type = "color_rgb"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: ColorRGBSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -396,7 +409,7 @@ class ColorRGBSelector(Selector[ColorRGBSelectorConfig]):
|
||||
return value
|
||||
|
||||
|
||||
class ColorTempSelectorConfig(TypedDict, total=False):
|
||||
class ColorTempSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a color temp selector config."""
|
||||
|
||||
unit: ColorTempSelectorUnit
|
||||
@ -419,7 +432,7 @@ class ColorTempSelector(Selector[ColorTempSelectorConfig]):
|
||||
|
||||
selector_type = "color_temp"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("unit", default=ColorTempSelectorUnit.MIRED): vol.All(
|
||||
vol.Coerce(ColorTempSelectorUnit), lambda val: val.value
|
||||
@ -456,7 +469,7 @@ class ColorTempSelector(Selector[ColorTempSelectorConfig]):
|
||||
return value
|
||||
|
||||
|
||||
class ConditionSelectorConfig(TypedDict):
|
||||
class ConditionSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent an condition selector config."""
|
||||
|
||||
|
||||
@ -466,7 +479,7 @@ class ConditionSelector(Selector[ConditionSelectorConfig]):
|
||||
|
||||
selector_type = "condition"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: ConditionSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -477,7 +490,7 @@ class ConditionSelector(Selector[ConditionSelectorConfig]):
|
||||
return vol.Schema(cv.CONDITIONS_SCHEMA)(data)
|
||||
|
||||
|
||||
class ConfigEntrySelectorConfig(TypedDict, total=False):
|
||||
class ConfigEntrySelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a config entry selector config."""
|
||||
|
||||
integration: str
|
||||
@ -489,7 +502,7 @@ class ConfigEntrySelector(Selector[ConfigEntrySelectorConfig]):
|
||||
|
||||
selector_type = "config_entry"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("integration"): str,
|
||||
}
|
||||
@ -505,7 +518,7 @@ class ConfigEntrySelector(Selector[ConfigEntrySelectorConfig]):
|
||||
return config
|
||||
|
||||
|
||||
class ConstantSelectorConfig(TypedDict, total=False):
|
||||
class ConstantSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a constant selector config."""
|
||||
|
||||
label: str
|
||||
@ -519,7 +532,7 @@ class ConstantSelector(Selector[ConstantSelectorConfig]):
|
||||
|
||||
selector_type = "constant"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("label"): str,
|
||||
vol.Optional("translation_key"): cv.string,
|
||||
@ -546,7 +559,7 @@ class QrErrorCorrectionLevel(StrEnum):
|
||||
HIGH = "high"
|
||||
|
||||
|
||||
class QrCodeSelectorConfig(TypedDict, total=False):
|
||||
class QrCodeSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a QR code selector config."""
|
||||
|
||||
data: str
|
||||
@ -560,7 +573,7 @@ class QrCodeSelector(Selector[QrCodeSelectorConfig]):
|
||||
|
||||
selector_type = "qr_code"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Required("data"): str,
|
||||
vol.Optional("scale"): int,
|
||||
@ -580,7 +593,7 @@ class QrCodeSelector(Selector[QrCodeSelectorConfig]):
|
||||
return self.config["data"]
|
||||
|
||||
|
||||
class ConversationAgentSelectorConfig(TypedDict, total=False):
|
||||
class ConversationAgentSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a conversation agent selector config."""
|
||||
|
||||
language: str
|
||||
@ -592,7 +605,7 @@ class ConversationAgentSelector(Selector[ConversationAgentSelectorConfig]):
|
||||
|
||||
selector_type = "conversation_agent"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("language"): str,
|
||||
}
|
||||
@ -608,7 +621,7 @@ class ConversationAgentSelector(Selector[ConversationAgentSelectorConfig]):
|
||||
return agent
|
||||
|
||||
|
||||
class CountrySelectorConfig(TypedDict, total=False):
|
||||
class CountrySelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a country selector config."""
|
||||
|
||||
countries: list[str]
|
||||
@ -621,7 +634,7 @@ class CountrySelector(Selector[CountrySelectorConfig]):
|
||||
|
||||
selector_type = "country"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("countries"): [str],
|
||||
vol.Optional("no_sort", default=False): cv.boolean,
|
||||
@ -642,7 +655,7 @@ class CountrySelector(Selector[CountrySelectorConfig]):
|
||||
return country
|
||||
|
||||
|
||||
class DateSelectorConfig(TypedDict):
|
||||
class DateSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a date selector config."""
|
||||
|
||||
|
||||
@ -652,7 +665,7 @@ class DateSelector(Selector[DateSelectorConfig]):
|
||||
|
||||
selector_type = "date"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: DateSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -664,7 +677,7 @@ class DateSelector(Selector[DateSelectorConfig]):
|
||||
return data
|
||||
|
||||
|
||||
class DateTimeSelectorConfig(TypedDict):
|
||||
class DateTimeSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a date time selector config."""
|
||||
|
||||
|
||||
@ -674,7 +687,7 @@ class DateTimeSelector(Selector[DateTimeSelectorConfig]):
|
||||
|
||||
selector_type = "datetime"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: DateTimeSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -686,7 +699,7 @@ class DateTimeSelector(Selector[DateTimeSelectorConfig]):
|
||||
return data
|
||||
|
||||
|
||||
class DeviceSelectorConfig(DeviceFilterSelectorConfig, total=False):
|
||||
class DeviceSelectorConfig(BaseSelectorConfig, DeviceFilterSelectorConfig, total=False):
|
||||
"""Class to represent a device selector config."""
|
||||
|
||||
entity: EntityFilterSelectorConfig | list[EntityFilterSelectorConfig]
|
||||
@ -700,7 +713,9 @@ class DeviceSelector(Selector[DeviceSelectorConfig]):
|
||||
|
||||
selector_type = "device"
|
||||
|
||||
CONFIG_SCHEMA = DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
DEVICE_FILTER_SELECTOR_CONFIG_SCHEMA.schema
|
||||
).extend(
|
||||
{
|
||||
vol.Optional("multiple", default=False): cv.boolean,
|
||||
vol.Optional("filter"): vol.All(
|
||||
@ -724,7 +739,7 @@ class DeviceSelector(Selector[DeviceSelectorConfig]):
|
||||
return [vol.Schema(str)(val) for val in data]
|
||||
|
||||
|
||||
class DurationSelectorConfig(TypedDict, total=False):
|
||||
class DurationSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a duration selector config."""
|
||||
|
||||
enable_day: bool
|
||||
@ -738,7 +753,7 @@ class DurationSelector(Selector[DurationSelectorConfig]):
|
||||
|
||||
selector_type = "duration"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
# Enable day field in frontend. A selection with `days` set is allowed
|
||||
# even if `enable_day` is not set
|
||||
@ -763,7 +778,7 @@ class DurationSelector(Selector[DurationSelectorConfig]):
|
||||
return cast(dict[str, float], data)
|
||||
|
||||
|
||||
class EntitySelectorConfig(EntityFilterSelectorConfig, total=False):
|
||||
class EntitySelectorConfig(BaseSelectorConfig, EntityFilterSelectorConfig, total=False):
|
||||
"""Class to represent an entity selector config."""
|
||||
|
||||
exclude_entities: list[str]
|
||||
@ -778,7 +793,9 @@ class EntitySelector(Selector[EntitySelectorConfig]):
|
||||
|
||||
selector_type = "entity"
|
||||
|
||||
CONFIG_SCHEMA = ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
ENTITY_FILTER_SELECTOR_CONFIG_SCHEMA.schema
|
||||
).extend(
|
||||
{
|
||||
vol.Optional("exclude_entities"): [str],
|
||||
vol.Optional("include_entities"): [str],
|
||||
@ -824,7 +841,7 @@ class EntitySelector(Selector[EntitySelectorConfig]):
|
||||
return cast(list, vol.Schema([validate])(data)) # Output is a list
|
||||
|
||||
|
||||
class FloorSelectorConfig(TypedDict, total=False):
|
||||
class FloorSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an floor selector config."""
|
||||
|
||||
entity: EntityFilterSelectorConfig | list[EntityFilterSelectorConfig]
|
||||
@ -838,7 +855,7 @@ class FloorSelector(Selector[FloorSelectorConfig]):
|
||||
|
||||
selector_type = "floor"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("entity"): vol.All(
|
||||
cv.ensure_list,
|
||||
@ -866,7 +883,7 @@ class FloorSelector(Selector[FloorSelectorConfig]):
|
||||
return [vol.Schema(str)(val) for val in data]
|
||||
|
||||
|
||||
class IconSelectorConfig(TypedDict, total=False):
|
||||
class IconSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an icon selector config."""
|
||||
|
||||
placeholder: str
|
||||
@ -878,7 +895,7 @@ class IconSelector(Selector[IconSelectorConfig]):
|
||||
|
||||
selector_type = "icon"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{vol.Optional("placeholder"): str}
|
||||
# Frontend also has a fallbackPath option, this is not used by core
|
||||
)
|
||||
@ -893,7 +910,7 @@ class IconSelector(Selector[IconSelectorConfig]):
|
||||
return icon
|
||||
|
||||
|
||||
class LabelSelectorConfig(TypedDict, total=False):
|
||||
class LabelSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a label selector config."""
|
||||
|
||||
multiple: bool
|
||||
@ -905,7 +922,7 @@ class LabelSelector(Selector[LabelSelectorConfig]):
|
||||
|
||||
selector_type = "label"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("multiple", default=False): cv.boolean,
|
||||
}
|
||||
@ -925,7 +942,7 @@ class LabelSelector(Selector[LabelSelectorConfig]):
|
||||
return [vol.Schema(str)(val) for val in data]
|
||||
|
||||
|
||||
class LanguageSelectorConfig(TypedDict, total=False):
|
||||
class LanguageSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an language selector config."""
|
||||
|
||||
languages: list[str]
|
||||
@ -939,7 +956,7 @@ class LanguageSelector(Selector[LanguageSelectorConfig]):
|
||||
|
||||
selector_type = "language"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("languages"): [str],
|
||||
vol.Optional("native_name", default=False): cv.boolean,
|
||||
@ -959,7 +976,7 @@ class LanguageSelector(Selector[LanguageSelectorConfig]):
|
||||
return language
|
||||
|
||||
|
||||
class LocationSelectorConfig(TypedDict, total=False):
|
||||
class LocationSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a location selector config."""
|
||||
|
||||
radius: bool
|
||||
@ -972,7 +989,7 @@ class LocationSelector(Selector[LocationSelectorConfig]):
|
||||
|
||||
selector_type = "location"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{vol.Optional("radius"): bool, vol.Optional("icon"): str}
|
||||
)
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
@ -993,7 +1010,7 @@ class LocationSelector(Selector[LocationSelectorConfig]):
|
||||
return location
|
||||
|
||||
|
||||
class MediaSelectorConfig(TypedDict):
|
||||
class MediaSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a media selector config."""
|
||||
|
||||
|
||||
@ -1003,7 +1020,7 @@ class MediaSelector(Selector[MediaSelectorConfig]):
|
||||
|
||||
selector_type = "media"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
# Although marked as optional in frontend, this field is required
|
||||
@ -1026,7 +1043,7 @@ class MediaSelector(Selector[MediaSelectorConfig]):
|
||||
return media
|
||||
|
||||
|
||||
class NumberSelectorConfig(TypedDict, total=False):
|
||||
class NumberSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a number selector config."""
|
||||
|
||||
min: float
|
||||
@ -1061,7 +1078,7 @@ class NumberSelector(Selector[NumberSelectorConfig]):
|
||||
selector_type = "number"
|
||||
|
||||
CONFIG_SCHEMA = vol.All(
|
||||
vol.Schema(
|
||||
BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("min"): vol.Coerce(float),
|
||||
vol.Optional("max"): vol.Coerce(float),
|
||||
@ -1096,7 +1113,7 @@ class NumberSelector(Selector[NumberSelectorConfig]):
|
||||
return value
|
||||
|
||||
|
||||
class ObjectSelectorConfig(TypedDict):
|
||||
class ObjectSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent an object selector config."""
|
||||
|
||||
|
||||
@ -1106,7 +1123,7 @@ class ObjectSelector(Selector[ObjectSelectorConfig]):
|
||||
|
||||
selector_type = "object"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: ObjectSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -1142,7 +1159,7 @@ class SelectSelectorMode(StrEnum):
|
||||
DROPDOWN = "dropdown"
|
||||
|
||||
|
||||
class SelectSelectorConfig(TypedDict, total=False):
|
||||
class SelectSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a select selector config."""
|
||||
|
||||
options: Required[Sequence[SelectOptionDict] | Sequence[str]]
|
||||
@ -1159,7 +1176,7 @@ class SelectSelector(Selector[SelectSelectorConfig]):
|
||||
|
||||
selector_type = "select"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Required("options"): vol.All(vol.Any([str], [select_option])),
|
||||
vol.Optional("multiple", default=False): cv.boolean,
|
||||
@ -1199,14 +1216,14 @@ class SelectSelector(Selector[SelectSelectorConfig]):
|
||||
return [parent_schema(vol.Schema(str)(val)) for val in data]
|
||||
|
||||
|
||||
class TargetSelectorConfig(TypedDict, total=False):
|
||||
class TargetSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a target selector config."""
|
||||
|
||||
entity: EntityFilterSelectorConfig | list[EntityFilterSelectorConfig]
|
||||
device: DeviceFilterSelectorConfig | list[DeviceFilterSelectorConfig]
|
||||
|
||||
|
||||
class StateSelectorConfig(TypedDict, total=False):
|
||||
class StateSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent an state selector config."""
|
||||
|
||||
entity_id: Required[str]
|
||||
@ -1218,7 +1235,7 @@ class StateSelector(Selector[StateSelectorConfig]):
|
||||
|
||||
selector_type = "state"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Required("entity_id"): cv.entity_id,
|
||||
# The attribute to filter on, is currently deliberately not
|
||||
@ -1248,7 +1265,7 @@ class TargetSelector(Selector[TargetSelectorConfig]):
|
||||
|
||||
selector_type = "target"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("entity"): vol.All(
|
||||
cv.ensure_list,
|
||||
@ -1273,7 +1290,7 @@ class TargetSelector(Selector[TargetSelectorConfig]):
|
||||
return target
|
||||
|
||||
|
||||
class TemplateSelectorConfig(TypedDict):
|
||||
class TemplateSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent an template selector config."""
|
||||
|
||||
|
||||
@ -1283,7 +1300,7 @@ class TemplateSelector(Selector[TemplateSelectorConfig]):
|
||||
|
||||
selector_type = "template"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: TemplateSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -1295,7 +1312,7 @@ class TemplateSelector(Selector[TemplateSelectorConfig]):
|
||||
return template.template
|
||||
|
||||
|
||||
class TextSelectorConfig(TypedDict, total=False):
|
||||
class TextSelectorConfig(BaseSelectorConfig, total=False):
|
||||
"""Class to represent a text selector config."""
|
||||
|
||||
multiline: bool
|
||||
@ -1330,7 +1347,7 @@ class TextSelector(Selector[TextSelectorConfig]):
|
||||
|
||||
selector_type = "text"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("multiline", default=False): bool,
|
||||
vol.Optional("prefix"): str,
|
||||
@ -1359,7 +1376,7 @@ class TextSelector(Selector[TextSelectorConfig]):
|
||||
return [vol.Schema(str)(val) for val in data]
|
||||
|
||||
|
||||
class ThemeSelectorConfig(TypedDict):
|
||||
class ThemeSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a theme selector config."""
|
||||
|
||||
|
||||
@ -1369,7 +1386,7 @@ class ThemeSelector(Selector[ThemeSelectorConfig]):
|
||||
|
||||
selector_type = "theme"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("include_default", default=False): cv.boolean,
|
||||
}
|
||||
@ -1385,7 +1402,7 @@ class ThemeSelector(Selector[ThemeSelectorConfig]):
|
||||
return theme
|
||||
|
||||
|
||||
class TimeSelectorConfig(TypedDict):
|
||||
class TimeSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a time selector config."""
|
||||
|
||||
|
||||
@ -1395,7 +1412,7 @@ class TimeSelector(Selector[TimeSelectorConfig]):
|
||||
|
||||
selector_type = "time"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: TimeSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -1407,7 +1424,7 @@ class TimeSelector(Selector[TimeSelectorConfig]):
|
||||
return cast(str, data)
|
||||
|
||||
|
||||
class TriggerSelectorConfig(TypedDict):
|
||||
class TriggerSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent an trigger selector config."""
|
||||
|
||||
|
||||
@ -1417,7 +1434,7 @@ class TriggerSelector(Selector[TriggerSelectorConfig]):
|
||||
|
||||
selector_type = "trigger"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA
|
||||
|
||||
def __init__(self, config: TriggerSelectorConfig | None = None) -> None:
|
||||
"""Instantiate a selector."""
|
||||
@ -1428,7 +1445,7 @@ class TriggerSelector(Selector[TriggerSelectorConfig]):
|
||||
return vol.Schema(cv.TRIGGER_SCHEMA)(data)
|
||||
|
||||
|
||||
class FileSelectorConfig(TypedDict):
|
||||
class FileSelectorConfig(BaseSelectorConfig):
|
||||
"""Class to represent a file selector config."""
|
||||
|
||||
accept: str # required
|
||||
@ -1440,7 +1457,7 @@ class FileSelector(Selector[FileSelectorConfig]):
|
||||
|
||||
selector_type = "file"
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
CONFIG_SCHEMA = BASE_SELECTOR_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept
|
||||
vol.Required("accept"): str,
|
||||
|
Loading…
x
Reference in New Issue
Block a user