mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Support in service descriptions for input sections (#116100)
This commit is contained in:
parent
c4b277b6ab
commit
75c7ae7c69
@ -199,45 +199,6 @@ turn_on:
|
||||
example: "[255, 100, 100]"
|
||||
selector:
|
||||
color_rgb:
|
||||
rgbw_color: &rgbw_color
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
example: "[255, 100, 100, 50]"
|
||||
selector:
|
||||
object:
|
||||
rgbww_color: &rgbww_color
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
example: "[255, 100, 100, 50, 70]"
|
||||
selector:
|
||||
object:
|
||||
color_name: &color_name
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
selector:
|
||||
select:
|
||||
translation_key: color_name
|
||||
options: *named_colors
|
||||
hs_color: &hs_color
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
example: "[300, 70]"
|
||||
selector:
|
||||
object:
|
||||
xy_color: &xy_color
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
example: "[0.52, 0.43]"
|
||||
selector:
|
||||
object:
|
||||
color_temp: &color_temp
|
||||
filter: *color_temp_support
|
||||
advanced: true
|
||||
selector:
|
||||
color_temp:
|
||||
unit: "mired"
|
||||
min: 153
|
||||
max: 500
|
||||
kelvin: &kelvin
|
||||
filter: *color_temp_support
|
||||
selector:
|
||||
@ -245,13 +206,6 @@ turn_on:
|
||||
unit: "kelvin"
|
||||
min: 2000
|
||||
max: 6500
|
||||
brightness: &brightness
|
||||
filter: *brightness_support
|
||||
advanced: true
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 255
|
||||
brightness_pct: &brightness_pct
|
||||
filter: *brightness_support
|
||||
selector:
|
||||
@ -259,13 +213,6 @@ turn_on:
|
||||
min: 0
|
||||
max: 100
|
||||
unit_of_measurement: "%"
|
||||
brightness_step:
|
||||
filter: *brightness_support
|
||||
advanced: true
|
||||
selector:
|
||||
number:
|
||||
min: -225
|
||||
max: 255
|
||||
brightness_step_pct:
|
||||
filter: *brightness_support
|
||||
selector:
|
||||
@ -273,39 +220,84 @@ turn_on:
|
||||
min: -100
|
||||
max: 100
|
||||
unit_of_measurement: "%"
|
||||
white: &white
|
||||
filter:
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.WHITE
|
||||
advanced: true
|
||||
selector:
|
||||
constant:
|
||||
value: true
|
||||
label: Enabled
|
||||
profile: &profile
|
||||
advanced: true
|
||||
example: relax
|
||||
selector:
|
||||
text:
|
||||
flash: &flash
|
||||
filter:
|
||||
supported_features:
|
||||
- light.LightEntityFeature.FLASH
|
||||
advanced: true
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- label: "Long"
|
||||
value: "long"
|
||||
- label: "Short"
|
||||
value: "short"
|
||||
effect: &effect
|
||||
filter:
|
||||
supported_features:
|
||||
- light.LightEntityFeature.EFFECT
|
||||
selector:
|
||||
text:
|
||||
advanced_fields:
|
||||
collapsed: true
|
||||
fields:
|
||||
rgbw_color: &rgbw_color
|
||||
filter: *color_support
|
||||
example: "[255, 100, 100, 50]"
|
||||
selector:
|
||||
object:
|
||||
rgbww_color: &rgbww_color
|
||||
filter: *color_support
|
||||
example: "[255, 100, 100, 50, 70]"
|
||||
selector:
|
||||
object:
|
||||
color_name: &color_name
|
||||
filter: *color_support
|
||||
selector:
|
||||
select:
|
||||
translation_key: color_name
|
||||
options: *named_colors
|
||||
hs_color: &hs_color
|
||||
filter: *color_support
|
||||
example: "[300, 70]"
|
||||
selector:
|
||||
object:
|
||||
xy_color: &xy_color
|
||||
filter: *color_support
|
||||
example: "[0.52, 0.43]"
|
||||
selector:
|
||||
object:
|
||||
color_temp: &color_temp
|
||||
filter: *color_temp_support
|
||||
selector:
|
||||
color_temp:
|
||||
unit: "mired"
|
||||
min: 153
|
||||
max: 500
|
||||
brightness: &brightness
|
||||
filter: *brightness_support
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 255
|
||||
brightness_step:
|
||||
filter: *brightness_support
|
||||
selector:
|
||||
number:
|
||||
min: -225
|
||||
max: 255
|
||||
white: &white
|
||||
filter:
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.WHITE
|
||||
selector:
|
||||
constant:
|
||||
value: true
|
||||
label: Enabled
|
||||
profile: &profile
|
||||
example: relax
|
||||
selector:
|
||||
text:
|
||||
flash: &flash
|
||||
filter:
|
||||
supported_features:
|
||||
- light.LightEntityFeature.FLASH
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- label: "Long"
|
||||
value: "long"
|
||||
- label: "Short"
|
||||
value: "short"
|
||||
|
||||
turn_off:
|
||||
target:
|
||||
@ -313,7 +305,10 @@ turn_off:
|
||||
domain: light
|
||||
fields:
|
||||
transition: *transition
|
||||
flash: *flash
|
||||
advanced_fields:
|
||||
collapsed: true
|
||||
fields:
|
||||
flash: *flash
|
||||
|
||||
toggle:
|
||||
target:
|
||||
@ -322,16 +317,19 @@ toggle:
|
||||
fields:
|
||||
transition: *transition
|
||||
rgb_color: *rgb_color
|
||||
rgbw_color: *rgbw_color
|
||||
rgbww_color: *rgbww_color
|
||||
color_name: *color_name
|
||||
hs_color: *hs_color
|
||||
xy_color: *xy_color
|
||||
color_temp: *color_temp
|
||||
kelvin: *kelvin
|
||||
brightness: *brightness
|
||||
brightness_pct: *brightness_pct
|
||||
white: *white
|
||||
profile: *profile
|
||||
flash: *flash
|
||||
effect: *effect
|
||||
advanced_fields:
|
||||
collapsed: true
|
||||
fields:
|
||||
rgbw_color: *rgbw_color
|
||||
rgbww_color: *rgbww_color
|
||||
color_name: *color_name
|
||||
hs_color: *hs_color
|
||||
xy_color: *xy_color
|
||||
color_temp: *color_temp
|
||||
brightness: *brightness
|
||||
white: *white
|
||||
profile: *profile
|
||||
flash: *flash
|
||||
|
@ -34,7 +34,8 @@
|
||||
"field_white_description": "Set the light to white mode.",
|
||||
"field_white_name": "White",
|
||||
"field_xy_color_description": "Color in XY-format. A list of two decimal numbers between 0 and 1.",
|
||||
"field_xy_color_name": "XY-color"
|
||||
"field_xy_color_name": "XY-color",
|
||||
"section_advanced_fields_name": "Advanced options"
|
||||
},
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
@ -354,6 +355,11 @@
|
||||
"name": "[%key:component::light::common::field_effect_name%]",
|
||||
"description": "[%key:component::light::common::field_effect_description%]"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"advanced_fields": {
|
||||
"name": "[%key:component::light::common::section_advanced_fields_name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"turn_off": {
|
||||
@ -368,6 +374,11 @@
|
||||
"name": "[%key:component::light::common::field_flash_name%]",
|
||||
"description": "[%key:component::light::common::field_flash_description%]"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"advanced_fields": {
|
||||
"name": "[%key:component::light::common::section_advanced_fields_name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"toggle": {
|
||||
@ -434,6 +445,11 @@
|
||||
"name": "[%key:component::light::common::field_effect_name%]",
|
||||
"description": "[%key:component::light::common::field_effect_description%]"
|
||||
}
|
||||
},
|
||||
"sections": {
|
||||
"advanced_fields": {
|
||||
"name": "[%key:component::light::common::section_advanced_fields_name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,10 +179,19 @@ _FIELD_SCHEMA = vol.Schema(
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
_SECTION_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required("fields"): vol.Schema({str: _FIELD_SCHEMA}),
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
_SERVICE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional("target"): vol.Any(TargetSelector.CONFIG_SCHEMA, None),
|
||||
vol.Optional("fields"): vol.Schema({str: _FIELD_SCHEMA}),
|
||||
vol.Optional("fields"): vol.Schema(
|
||||
{str: vol.Any(_SECTION_SCHEMA, _FIELD_SCHEMA)}
|
||||
),
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
@ -26,6 +26,23 @@ def exists(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
|
||||
def unique_field_validator(fields: Any) -> Any:
|
||||
"""Validate the inputs don't have duplicate keys under different sections."""
|
||||
all_fields = set()
|
||||
for key, value in fields.items():
|
||||
if value and "fields" in value:
|
||||
for key in value["fields"]:
|
||||
if key in all_fields:
|
||||
raise vol.Invalid(f"Duplicate use of field {key} in service.")
|
||||
all_fields.add(key)
|
||||
else:
|
||||
if key in all_fields:
|
||||
raise vol.Invalid(f"Duplicate use of field {key} in service.")
|
||||
all_fields.add(key)
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
CORE_INTEGRATION_FIELD_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional("example"): exists,
|
||||
@ -44,6 +61,13 @@ CORE_INTEGRATION_FIELD_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
CORE_INTEGRATION_SECTION_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional("collapsed"): bool,
|
||||
vol.Required("fields"): vol.Schema({str: CORE_INTEGRATION_FIELD_SCHEMA}),
|
||||
}
|
||||
)
|
||||
|
||||
CUSTOM_INTEGRATION_FIELD_SCHEMA = CORE_INTEGRATION_FIELD_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional("description"): str,
|
||||
@ -57,7 +81,17 @@ CORE_INTEGRATION_SERVICE_SCHEMA = vol.Any(
|
||||
vol.Optional("target"): vol.Any(
|
||||
selector.TargetSelector.CONFIG_SCHEMA, None
|
||||
),
|
||||
vol.Optional("fields"): vol.Schema({str: CORE_INTEGRATION_FIELD_SCHEMA}),
|
||||
vol.Optional("fields"): vol.All(
|
||||
vol.Schema(
|
||||
{
|
||||
str: vol.Any(
|
||||
CORE_INTEGRATION_FIELD_SCHEMA,
|
||||
CORE_INTEGRATION_SECTION_SCHEMA,
|
||||
)
|
||||
}
|
||||
),
|
||||
unique_field_validator,
|
||||
),
|
||||
}
|
||||
),
|
||||
None,
|
||||
@ -107,7 +141,7 @@ def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) -> bool
|
||||
return False
|
||||
|
||||
|
||||
def validate_services(config: Config, integration: Integration) -> None:
|
||||
def validate_services(config: Config, integration: Integration) -> None: # noqa: C901
|
||||
"""Validate services."""
|
||||
try:
|
||||
data = load_yaml_dict(str(integration.path / "services.yaml"))
|
||||
@ -200,6 +234,9 @@ def validate_services(config: Config, integration: Integration) -> None:
|
||||
# The same check is done for the description in each of the fields of the
|
||||
# service schema.
|
||||
for field_name, field_schema in service_schema.get("fields", {}).items():
|
||||
if "fields" in field_schema:
|
||||
# This is a section
|
||||
continue
|
||||
if "name" not in field_schema:
|
||||
try:
|
||||
strings["services"][service_name]["fields"][field_name]["name"]
|
||||
@ -233,6 +270,20 @@ def validate_services(config: Config, integration: Integration) -> None:
|
||||
f"Service {service_name} has a field {field_name} with a selector with a translation key {translation_key} that is not in the translations file",
|
||||
)
|
||||
|
||||
# The same check is done for the description in each of the sections of the
|
||||
# service schema.
|
||||
for section_name, section_schema in service_schema.get("fields", {}).items():
|
||||
if "fields" not in section_schema:
|
||||
# This is not a section
|
||||
continue
|
||||
try:
|
||||
strings["services"][service_name]["sections"][section_name]["name"]
|
||||
except KeyError:
|
||||
integration.add_error(
|
||||
"services",
|
||||
f"Service {service_name} has a section {section_name} with no name {error_msg_suffix}",
|
||||
)
|
||||
|
||||
|
||||
def validate(integrations: dict[str, Integration], config: Config) -> None:
|
||||
"""Handle dependencies for integrations."""
|
||||
|
@ -383,6 +383,13 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||
},
|
||||
slug_validator=translation_key_validator,
|
||||
),
|
||||
vol.Optional("sections"): cv.schema_with_slug_keys(
|
||||
{
|
||||
vol.Required("name"): str,
|
||||
vol.Optional("description"): translation_value_validator,
|
||||
},
|
||||
slug_validator=translation_key_validator,
|
||||
),
|
||||
},
|
||||
slug_validator=translation_key_validator,
|
||||
),
|
||||
|
@ -990,6 +990,17 @@ async def test_async_get_all_descriptions_filter(hass: HomeAssistant) -> None:
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
selector:
|
||||
number:
|
||||
advanced_stuff:
|
||||
fields:
|
||||
temperature:
|
||||
filter:
|
||||
supported_features:
|
||||
- alarm_control_panel.AlarmControlPanelEntityFeature.ARM_HOME
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
selector:
|
||||
number:
|
||||
"""
|
||||
|
||||
domain = "test_domain"
|
||||
@ -1024,6 +1035,17 @@ async def test_async_get_all_descriptions_filter(hass: HomeAssistant) -> None:
|
||||
test_service_schema = {
|
||||
"description": "",
|
||||
"fields": {
|
||||
"advanced_stuff": {
|
||||
"fields": {
|
||||
"temperature": {
|
||||
"filter": {
|
||||
"attribute": {"supported_color_modes": ["color_temp"]},
|
||||
"supported_features": [1],
|
||||
},
|
||||
"selector": {"number": None},
|
||||
},
|
||||
},
|
||||
},
|
||||
"temperature": {
|
||||
"filter": {
|
||||
"attribute": {"supported_color_modes": ["color_temp"]},
|
||||
|
Loading…
x
Reference in New Issue
Block a user