Support shared keys starting with period in services.yaml (#118789)
This commit is contained in:
parent
ea571a6997
commit
8620bef5b0
5 changed files with 262 additions and 181 deletions
|
@ -1,4 +1,184 @@
|
|||
# Describes the format for available light services
|
||||
.brightness_support: &brightness_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.BRIGHTNESS
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
|
||||
.color_support: &color_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
|
||||
.color_temp_support: &color_temp_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
|
||||
.named_colors: &named_colors
|
||||
- "homeassistant"
|
||||
- "aliceblue"
|
||||
- "antiquewhite"
|
||||
- "aqua"
|
||||
- "aquamarine"
|
||||
- "azure"
|
||||
- "beige"
|
||||
- "bisque"
|
||||
# Black is omitted from this list as nonsensical for lights
|
||||
- "blanchedalmond"
|
||||
- "blue"
|
||||
- "blueviolet"
|
||||
- "brown"
|
||||
- "burlywood"
|
||||
- "cadetblue"
|
||||
- "chartreuse"
|
||||
- "chocolate"
|
||||
- "coral"
|
||||
- "cornflowerblue"
|
||||
- "cornsilk"
|
||||
- "crimson"
|
||||
- "cyan"
|
||||
- "darkblue"
|
||||
- "darkcyan"
|
||||
- "darkgoldenrod"
|
||||
- "darkgray"
|
||||
- "darkgreen"
|
||||
- "darkgrey"
|
||||
- "darkkhaki"
|
||||
- "darkmagenta"
|
||||
- "darkolivegreen"
|
||||
- "darkorange"
|
||||
- "darkorchid"
|
||||
- "darkred"
|
||||
- "darksalmon"
|
||||
- "darkseagreen"
|
||||
- "darkslateblue"
|
||||
- "darkslategray"
|
||||
- "darkslategrey"
|
||||
- "darkturquoise"
|
||||
- "darkviolet"
|
||||
- "deeppink"
|
||||
- "deepskyblue"
|
||||
- "dimgray"
|
||||
- "dimgrey"
|
||||
- "dodgerblue"
|
||||
- "firebrick"
|
||||
- "floralwhite"
|
||||
- "forestgreen"
|
||||
- "fuchsia"
|
||||
- "gainsboro"
|
||||
- "ghostwhite"
|
||||
- "gold"
|
||||
- "goldenrod"
|
||||
- "gray"
|
||||
- "green"
|
||||
- "greenyellow"
|
||||
- "grey"
|
||||
- "honeydew"
|
||||
- "hotpink"
|
||||
- "indianred"
|
||||
- "indigo"
|
||||
- "ivory"
|
||||
- "khaki"
|
||||
- "lavender"
|
||||
- "lavenderblush"
|
||||
- "lawngreen"
|
||||
- "lemonchiffon"
|
||||
- "lightblue"
|
||||
- "lightcoral"
|
||||
- "lightcyan"
|
||||
- "lightgoldenrodyellow"
|
||||
- "lightgray"
|
||||
- "lightgreen"
|
||||
- "lightgrey"
|
||||
- "lightpink"
|
||||
- "lightsalmon"
|
||||
- "lightseagreen"
|
||||
- "lightskyblue"
|
||||
- "lightslategray"
|
||||
- "lightslategrey"
|
||||
- "lightsteelblue"
|
||||
- "lightyellow"
|
||||
- "lime"
|
||||
- "limegreen"
|
||||
- "linen"
|
||||
- "magenta"
|
||||
- "maroon"
|
||||
- "mediumaquamarine"
|
||||
- "mediumblue"
|
||||
- "mediumorchid"
|
||||
- "mediumpurple"
|
||||
- "mediumseagreen"
|
||||
- "mediumslateblue"
|
||||
- "mediumspringgreen"
|
||||
- "mediumturquoise"
|
||||
- "mediumvioletred"
|
||||
- "midnightblue"
|
||||
- "mintcream"
|
||||
- "mistyrose"
|
||||
- "moccasin"
|
||||
- "navajowhite"
|
||||
- "navy"
|
||||
- "navyblue"
|
||||
- "oldlace"
|
||||
- "olive"
|
||||
- "olivedrab"
|
||||
- "orange"
|
||||
- "orangered"
|
||||
- "orchid"
|
||||
- "palegoldenrod"
|
||||
- "palegreen"
|
||||
- "paleturquoise"
|
||||
- "palevioletred"
|
||||
- "papayawhip"
|
||||
- "peachpuff"
|
||||
- "peru"
|
||||
- "pink"
|
||||
- "plum"
|
||||
- "powderblue"
|
||||
- "purple"
|
||||
- "red"
|
||||
- "rosybrown"
|
||||
- "royalblue"
|
||||
- "saddlebrown"
|
||||
- "salmon"
|
||||
- "sandybrown"
|
||||
- "seagreen"
|
||||
- "seashell"
|
||||
- "sienna"
|
||||
- "silver"
|
||||
- "skyblue"
|
||||
- "slateblue"
|
||||
- "slategray"
|
||||
- "slategrey"
|
||||
- "snow"
|
||||
- "springgreen"
|
||||
- "steelblue"
|
||||
- "tan"
|
||||
- "teal"
|
||||
- "thistle"
|
||||
- "tomato"
|
||||
- "turquoise"
|
||||
- "violet"
|
||||
- "wheat"
|
||||
- "white"
|
||||
- "whitesmoke"
|
||||
- "yellow"
|
||||
- "yellowgreen"
|
||||
|
||||
turn_on:
|
||||
target:
|
||||
|
@ -15,14 +195,7 @@ turn_on:
|
|||
max: 300
|
||||
unit_of_measurement: seconds
|
||||
rgb_color: &rgb_color
|
||||
filter: &color_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
filter: *color_support
|
||||
example: "[255, 100, 100]"
|
||||
selector:
|
||||
color_rgb:
|
||||
|
@ -44,156 +217,7 @@ turn_on:
|
|||
selector:
|
||||
select:
|
||||
translation_key: color_name
|
||||
options: &named_colors
|
||||
- "homeassistant"
|
||||
- "aliceblue"
|
||||
- "antiquewhite"
|
||||
- "aqua"
|
||||
- "aquamarine"
|
||||
- "azure"
|
||||
- "beige"
|
||||
- "bisque"
|
||||
# Black is omitted from this list as nonsensical for lights
|
||||
- "blanchedalmond"
|
||||
- "blue"
|
||||
- "blueviolet"
|
||||
- "brown"
|
||||
- "burlywood"
|
||||
- "cadetblue"
|
||||
- "chartreuse"
|
||||
- "chocolate"
|
||||
- "coral"
|
||||
- "cornflowerblue"
|
||||
- "cornsilk"
|
||||
- "crimson"
|
||||
- "cyan"
|
||||
- "darkblue"
|
||||
- "darkcyan"
|
||||
- "darkgoldenrod"
|
||||
- "darkgray"
|
||||
- "darkgreen"
|
||||
- "darkgrey"
|
||||
- "darkkhaki"
|
||||
- "darkmagenta"
|
||||
- "darkolivegreen"
|
||||
- "darkorange"
|
||||
- "darkorchid"
|
||||
- "darkred"
|
||||
- "darksalmon"
|
||||
- "darkseagreen"
|
||||
- "darkslateblue"
|
||||
- "darkslategray"
|
||||
- "darkslategrey"
|
||||
- "darkturquoise"
|
||||
- "darkviolet"
|
||||
- "deeppink"
|
||||
- "deepskyblue"
|
||||
- "dimgray"
|
||||
- "dimgrey"
|
||||
- "dodgerblue"
|
||||
- "firebrick"
|
||||
- "floralwhite"
|
||||
- "forestgreen"
|
||||
- "fuchsia"
|
||||
- "gainsboro"
|
||||
- "ghostwhite"
|
||||
- "gold"
|
||||
- "goldenrod"
|
||||
- "gray"
|
||||
- "green"
|
||||
- "greenyellow"
|
||||
- "grey"
|
||||
- "honeydew"
|
||||
- "hotpink"
|
||||
- "indianred"
|
||||
- "indigo"
|
||||
- "ivory"
|
||||
- "khaki"
|
||||
- "lavender"
|
||||
- "lavenderblush"
|
||||
- "lawngreen"
|
||||
- "lemonchiffon"
|
||||
- "lightblue"
|
||||
- "lightcoral"
|
||||
- "lightcyan"
|
||||
- "lightgoldenrodyellow"
|
||||
- "lightgray"
|
||||
- "lightgreen"
|
||||
- "lightgrey"
|
||||
- "lightpink"
|
||||
- "lightsalmon"
|
||||
- "lightseagreen"
|
||||
- "lightskyblue"
|
||||
- "lightslategray"
|
||||
- "lightslategrey"
|
||||
- "lightsteelblue"
|
||||
- "lightyellow"
|
||||
- "lime"
|
||||
- "limegreen"
|
||||
- "linen"
|
||||
- "magenta"
|
||||
- "maroon"
|
||||
- "mediumaquamarine"
|
||||
- "mediumblue"
|
||||
- "mediumorchid"
|
||||
- "mediumpurple"
|
||||
- "mediumseagreen"
|
||||
- "mediumslateblue"
|
||||
- "mediumspringgreen"
|
||||
- "mediumturquoise"
|
||||
- "mediumvioletred"
|
||||
- "midnightblue"
|
||||
- "mintcream"
|
||||
- "mistyrose"
|
||||
- "moccasin"
|
||||
- "navajowhite"
|
||||
- "navy"
|
||||
- "navyblue"
|
||||
- "oldlace"
|
||||
- "olive"
|
||||
- "olivedrab"
|
||||
- "orange"
|
||||
- "orangered"
|
||||
- "orchid"
|
||||
- "palegoldenrod"
|
||||
- "palegreen"
|
||||
- "paleturquoise"
|
||||
- "palevioletred"
|
||||
- "papayawhip"
|
||||
- "peachpuff"
|
||||
- "peru"
|
||||
- "pink"
|
||||
- "plum"
|
||||
- "powderblue"
|
||||
- "purple"
|
||||
- "red"
|
||||
- "rosybrown"
|
||||
- "royalblue"
|
||||
- "saddlebrown"
|
||||
- "salmon"
|
||||
- "sandybrown"
|
||||
- "seagreen"
|
||||
- "seashell"
|
||||
- "sienna"
|
||||
- "silver"
|
||||
- "skyblue"
|
||||
- "slateblue"
|
||||
- "slategray"
|
||||
- "slategrey"
|
||||
- "snow"
|
||||
- "springgreen"
|
||||
- "steelblue"
|
||||
- "tan"
|
||||
- "teal"
|
||||
- "thistle"
|
||||
- "tomato"
|
||||
- "turquoise"
|
||||
- "violet"
|
||||
- "wheat"
|
||||
- "white"
|
||||
- "whitesmoke"
|
||||
- "yellow"
|
||||
- "yellowgreen"
|
||||
options: *named_colors
|
||||
hs_color: &hs_color
|
||||
filter: *color_support
|
||||
advanced: true
|
||||
|
@ -207,15 +231,7 @@ turn_on:
|
|||
selector:
|
||||
object:
|
||||
color_temp: &color_temp
|
||||
filter: &color_temp_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
filter: *color_temp_support
|
||||
advanced: true
|
||||
selector:
|
||||
color_temp:
|
||||
|
@ -230,16 +246,7 @@ turn_on:
|
|||
min: 2000
|
||||
max: 6500
|
||||
brightness: &brightness
|
||||
filter: &brightness_support
|
||||
attribute:
|
||||
supported_color_modes:
|
||||
- light.ColorMode.BRIGHTNESS
|
||||
- light.ColorMode.COLOR_TEMP
|
||||
- light.ColorMode.HS
|
||||
- light.ColorMode.XY
|
||||
- light.ColorMode.RGB
|
||||
- light.ColorMode.RGBW
|
||||
- light.ColorMode.RGBWW
|
||||
filter: *brightness_support
|
||||
advanced: true
|
||||
selector:
|
||||
number:
|
||||
|
|
|
@ -187,7 +187,20 @@ _SERVICE_SCHEMA = vol.Schema(
|
|||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
_SERVICES_SCHEMA = vol.Schema({cv.slug: vol.Any(None, _SERVICE_SCHEMA)})
|
||||
|
||||
def starts_with_dot(key: str) -> str:
|
||||
"""Check if key starts with dot."""
|
||||
if not key.startswith("."):
|
||||
raise vol.Invalid("Key does not start with .")
|
||||
return key
|
||||
|
||||
|
||||
_SERVICES_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Remove(vol.All(str, starts_with_dot)): object,
|
||||
cv.slug: vol.Any(None, _SERVICE_SCHEMA),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ServiceParams(TypedDict):
|
||||
|
|
|
@ -78,7 +78,10 @@ CUSTOM_INTEGRATION_SERVICE_SCHEMA = vol.Any(
|
|||
)
|
||||
|
||||
CORE_INTEGRATION_SERVICES_SCHEMA = vol.Schema(
|
||||
{cv.slug: CORE_INTEGRATION_SERVICE_SCHEMA}
|
||||
{
|
||||
vol.Remove(vol.All(str, service.starts_with_dot)): object,
|
||||
cv.slug: CORE_INTEGRATION_SERVICE_SCHEMA,
|
||||
}
|
||||
)
|
||||
CUSTOM_INTEGRATION_SERVICES_SCHEMA = vol.Schema(
|
||||
{cv.slug: CUSTOM_INTEGRATION_SERVICE_SCHEMA}
|
||||
|
|
|
@ -1432,7 +1432,10 @@ def mock_config_flow(domain: str, config_flow: type[ConfigFlow]) -> None:
|
|||
|
||||
|
||||
def mock_integration(
|
||||
hass: HomeAssistant, module: MockModule, built_in: bool = True
|
||||
hass: HomeAssistant,
|
||||
module: MockModule,
|
||||
built_in: bool = True,
|
||||
top_level_files: set[str] | None = None,
|
||||
) -> loader.Integration:
|
||||
"""Mock an integration."""
|
||||
integration = loader.Integration(
|
||||
|
@ -1442,7 +1445,7 @@ def mock_integration(
|
|||
else f"{loader.PACKAGE_CUSTOM_COMPONENTS}.{module.DOMAIN}",
|
||||
pathlib.Path(""),
|
||||
module.mock_manifest(),
|
||||
set(),
|
||||
top_level_files,
|
||||
)
|
||||
|
||||
def mock_import_platform(platform_name: str) -> NoReturn:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import asyncio
|
||||
from collections.abc import Iterable
|
||||
from copy import deepcopy
|
||||
import io
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
|
@ -43,13 +44,16 @@ from homeassistant.helpers import (
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.loader import async_get_integration
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.yaml.loader import parse_yaml
|
||||
|
||||
from tests.common import (
|
||||
MockEntity,
|
||||
MockModule,
|
||||
MockUser,
|
||||
async_mock_service,
|
||||
mock_area_registry,
|
||||
mock_device_registry,
|
||||
mock_integration,
|
||||
mock_registry,
|
||||
)
|
||||
|
||||
|
@ -916,6 +920,57 @@ async def test_async_get_all_descriptions(hass: HomeAssistant) -> None:
|
|||
assert await service.async_get_all_descriptions(hass) is descriptions
|
||||
|
||||
|
||||
async def test_async_get_all_descriptions_dot_keys(hass: HomeAssistant) -> None:
|
||||
"""Test async_get_all_descriptions with keys starting with a period."""
|
||||
service_descriptions = """
|
||||
.anchor: &anchor
|
||||
selector:
|
||||
text:
|
||||
test_service:
|
||||
fields:
|
||||
test: *anchor
|
||||
"""
|
||||
|
||||
domain = "test_domain"
|
||||
|
||||
hass.services.async_register(domain, "test_service", lambda call: None)
|
||||
mock_integration(hass, MockModule(domain), top_level_files={"services.yaml"})
|
||||
assert await async_setup_component(hass, domain, {})
|
||||
|
||||
def load_yaml(fname, secrets=None):
|
||||
with io.StringIO(service_descriptions) as file:
|
||||
return parse_yaml(file)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.helpers.service._load_services_files",
|
||||
side_effect=service._load_services_files,
|
||||
) as proxy_load_services_files,
|
||||
patch(
|
||||
"homeassistant.util.yaml.loader.load_yaml",
|
||||
side_effect=load_yaml,
|
||||
) as mock_load_yaml,
|
||||
):
|
||||
descriptions = await service.async_get_all_descriptions(hass)
|
||||
|
||||
mock_load_yaml.assert_called_once_with("services.yaml", None)
|
||||
assert proxy_load_services_files.mock_calls[0][1][1] == unordered(
|
||||
[
|
||||
await async_get_integration(hass, domain),
|
||||
]
|
||||
)
|
||||
|
||||
assert descriptions == {
|
||||
"test_domain": {
|
||||
"test_service": {
|
||||
"description": "",
|
||||
"fields": {"test": {"selector": {"text": None}}},
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def test_async_get_all_descriptions_failing_integration(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
|
|
Loading…
Add table
Reference in a new issue