From 93cb1e01f5c2ac1611d568a45bb1645823bd2761 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 6 Jul 2023 11:31:04 +0200 Subject: [PATCH 01/11] Add support for service translations --- homeassistant/components/light/services.yaml | 82 ---------- homeassistant/components/light/strings.json | 148 +++++++++++++++++++ script/hassfest/services.py | 50 ++++++- script/hassfest/translations.py | 14 ++ 4 files changed, 207 insertions(+), 87 deletions(-) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index d1221dd121071e..ad14162f391264 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -1,17 +1,11 @@ # Describes the format for available light services turn_on: - name: Turn on - description: > - Turn on one or more lights and adjust properties of the light, even when - they are turned on already. target: entity: domain: light fields: transition: - name: Transition - description: Duration it takes to get to next state. filter: supported_features: - light.LightEntityFeature.TRANSITION @@ -21,8 +15,6 @@ turn_on: max: 300 unit_of_measurement: seconds rgb_color: - name: Color - description: The color for the light (based on RGB - red, green, blue). filter: attribute: supported_color_modes: @@ -34,8 +26,6 @@ turn_on: selector: color_rgb: rgbw_color: - name: RGBW-color - description: A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color for the light. filter: attribute: supported_color_modes: @@ -49,8 +39,6 @@ turn_on: selector: object: rgbww_color: - name: RGBWW-color - description: A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color for the light. filter: attribute: supported_color_modes: @@ -64,8 +52,6 @@ turn_on: selector: object: color_name: - name: Color name - description: A human readable color name. filter: attribute: supported_color_modes: @@ -228,8 +214,6 @@ turn_on: - "yellow" - "yellowgreen" hs_color: - name: Hue/Sat color - description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100. filter: attribute: supported_color_modes: @@ -243,8 +227,6 @@ turn_on: selector: object: xy_color: - name: XY-color - description: Color for the light in XY-format. filter: attribute: supported_color_modes: @@ -258,8 +240,6 @@ turn_on: selector: object: color_temp: - name: Color temperature - description: Color temperature for the light in mireds. filter: attribute: supported_color_modes: @@ -274,8 +254,6 @@ turn_on: min_mireds: 153 max_mireds: 500 kelvin: - name: Color temperature (Kelvin) - description: Color temperature for the light in Kelvin. filter: attribute: supported_color_modes: @@ -293,10 +271,6 @@ turn_on: step: 100 unit_of_measurement: K brightness: - name: Brightness value - description: Number indicating brightness, where 0 turns the light - off, 1 is the minimum brightness and 255 is the maximum brightness - supported by the light. filter: attribute: supported_color_modes: @@ -313,10 +287,6 @@ turn_on: min: 0 max: 255 brightness_pct: - name: Brightness - description: Number indicating percentage of full brightness, where 0 - turns the light off, 1 is the minimum brightness and 100 is the maximum - brightness supported by the light. filter: attribute: supported_color_modes: @@ -333,8 +303,6 @@ turn_on: max: 100 unit_of_measurement: "%" brightness_step: - name: Brightness step value - description: Change brightness by an amount. filter: attribute: supported_color_modes: @@ -351,8 +319,6 @@ turn_on: min: -225 max: 255 brightness_step_pct: - name: Brightness step - description: Change brightness by a percentage. filter: attribute: supported_color_modes: @@ -369,8 +335,6 @@ turn_on: max: 100 unit_of_measurement: "%" white: - name: White - description: Set the light to white mode. filter: attribute: supported_color_modes: @@ -381,15 +345,11 @@ turn_on: value: true label: Enabled profile: - name: Profile - description: Name of a light profile to use. advanced: true example: relax selector: text: flash: - name: Flash - description: If the light should flash. filter: supported_features: - light.LightEntityFeature.FLASH @@ -402,8 +362,6 @@ turn_on: - label: "Short" value: "short" effect: - name: Effect - description: Light effect. filter: supported_features: - light.LightEntityFeature.EFFECT @@ -411,15 +369,11 @@ turn_on: text: turn_off: - name: Turn off - description: Turns off one or more lights. target: entity: domain: light fields: transition: - name: Transition - description: Duration it takes to get to next state. filter: supported_features: - light.LightEntityFeature.TRANSITION @@ -429,8 +383,6 @@ turn_off: max: 300 unit_of_measurement: seconds flash: - name: Flash - description: If the light should flash. filter: supported_features: - light.LightEntityFeature.FLASH @@ -444,17 +396,11 @@ turn_off: value: "short" toggle: - name: Toggle - description: > - Toggles one or more lights, from on to off, or, off to on, based on their - current state. target: entity: domain: light fields: transition: - name: Transition - description: Duration it takes to get to next state. filter: supported_features: - light.LightEntityFeature.TRANSITION @@ -464,8 +410,6 @@ toggle: max: 300 unit_of_measurement: seconds rgb_color: - name: RGB-color - description: Color for the light in RGB-format. filter: attribute: supported_color_modes: @@ -479,8 +423,6 @@ toggle: selector: object: color_name: - name: Color name - description: A human readable color name. filter: attribute: supported_color_modes: @@ -643,8 +585,6 @@ toggle: - "yellow" - "yellowgreen" hs_color: - name: Hue/Sat color - description: Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100. filter: attribute: supported_color_modes: @@ -658,8 +598,6 @@ toggle: selector: object: xy_color: - name: XY-color - description: Color for the light in XY-format. filter: attribute: supported_color_modes: @@ -673,8 +611,6 @@ toggle: selector: object: color_temp: - name: Color temperature (mireds) - description: Color temperature for the light in mireds. filter: attribute: supported_color_modes: @@ -688,8 +624,6 @@ toggle: selector: color_temp: kelvin: - name: Color temperature (Kelvin) - description: Color temperature for the light in Kelvin. filter: attribute: supported_color_modes: @@ -707,10 +641,6 @@ toggle: step: 100 unit_of_measurement: K brightness: - name: Brightness value - description: Number indicating brightness, where 0 turns the light - off, 1 is the minimum brightness and 255 is the maximum brightness - supported by the light. filter: attribute: supported_color_modes: @@ -727,10 +657,6 @@ toggle: min: 0 max: 255 brightness_pct: - name: Brightness - description: Number indicating percentage of full brightness, where 0 - turns the light off, 1 is the minimum brightness and 100 is the maximum - brightness supported by the light. filter: attribute: supported_color_modes: @@ -747,8 +673,6 @@ toggle: max: 100 unit_of_measurement: "%" white: - name: White - description: Set the light to white mode. filter: attribute: supported_color_modes: @@ -759,15 +683,11 @@ toggle: value: true label: Enabled profile: - name: Profile - description: Name of a light profile to use. advanced: true example: relax selector: text: flash: - name: Flash - description: If the light should flash. filter: supported_features: - light.LightEntityFeature.FLASH @@ -780,8 +700,6 @@ toggle: - label: "Short" value: "short" effect: - name: Effect - description: Light effect. filter: supported_features: - light.LightEntityFeature.EFFECT diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index 6219ade3e58384..bd6a695999a2e9 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -86,5 +86,153 @@ } } } + }, + "services": { + "turn_on": { + "name": "Turn on", + "description": "Turn on one or more lights and adjust properties of the light, even when they are turned on already.", + "fields": { + "transition": { + "name": "Transition", + "description": "Duration it takes to get to next state." + }, + "rgb_color": { + "name": "Color", + "description": "The color for the light (based on RGB - red, green, blue)." + }, + "rgbw_color": { + "name": "RGBW-color", + "description": "A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color for the light." + }, + "rgbww_color": { + "name": "RGBWW-color", + "description": "A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color for the light." + }, + "color_name": { + "name": "Color name", + "description": "A human readable color name." + }, + "hs_color": { + "name": "Hue/Sat color", + "description": "Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100." + }, + "xy_color": { + "name": "XY-color", + "description": "Color for the light in XY-format." + }, + "color_temp": { + "name": "Color temperature", + "description": "Color temperature for the light in mireds." + }, + "kelvin": { + "name": "Color temperature (Kelvin)", + "description": "Color temperature for the light in Kelvin." + }, + "brightness": { + "name": "Brightness value", + "description": "Number indicating brightness, where 0 turns the light off, 1 is the minimum brightness and 255 is the maximum brightness supported by the light." + }, + "brightness_pct": { + "name": "Brightness", + "description": "Number indicating percentage of full brightness, where 0 turns the light off, 1 is the minimum brightness and 100 is the maximum brightness supported by the light." + }, + "brightness_step": { + "name": "Brightness step value", + "description": "Change brightness by an amount." + }, + "brightness_step_pct": { + "name": "Brightness step", + "description": "Change brightness by a percentage." + }, + "white": { + "name": "White", + "description": "Set the light to white mode." + }, + "profile": { + "name": "Profile", + "description": "Name of a light profile to use." + }, + "flash": { + "name": "Flash", + "description": "If the light should flash." + }, + "effect": { + "name": "Effect", + "description": "Light effect." + } + } + }, + "turn_off": { + "name": "Turn off", + "description": "Turn off one or more lights.", + "fields": { + "transition": { + "name": "[%key:component::light::services::turn_on::fields::transition::name%]", + "description": "[%key:component::light::services::turn_on::fields::transition::description%]" + }, + "flash": { + "name": "[%key:component::light::services::turn_on::fields::flash::name%]", + "description": "[%key:component::light::services::turn_on::fields::flash::description%]" + } + } + }, + "toggle": { + "name": "Toggle", + "description": "Toggles one or more lights, from on to off, or, off to on, based on their current state.", + "fields": { + "transition": { + "name": "[%key:component::light::services::turn_on::fields::transition::name%]", + "description": "[%key:component::light::services::turn_on::fields::transition::description%]" + }, + "rgb_color": { + "name": "[%key:component::light::services::turn_on::fields::rgb_color::name%]", + "description": "[%key:component::light::services::turn_on::fields::rgb_color::description%]" + }, + "color_name": { + "name": "[%key:component::light::services::turn_on::fields::color_name::name%]", + "description": "[%key:component::light::services::turn_on::fields::color_name::description%]" + }, + "hs_color": { + "name": "[%key:component::light::services::turn_on::fields::hs_color::name%]", + "description": "[%key:component::light::services::turn_on::fields::hs_color::description%]" + }, + "xy_color": { + "name": "[%key:component::light::services::turn_on::fields::xy_color::name%]", + "description": "[%key:component::light::services::turn_on::fields::xy_color::description%]" + }, + "color_temp": { + "name": "[%key:component::light::services::turn_on::fields::color_temp::name%]", + "description": "[%key:component::light::services::turn_on::fields::color_temp::description%]" + }, + "kelvin": { + "name": "[%key:component::light::services::turn_on::fields::kelvin::name%]", + "description": "[%key:component::light::services::turn_on::fields::kelvin::description%]" + }, + "brightness": { + "name": "[%key:component::light::services::turn_on::fields::brightness::name%]", + "description": "[%key:component::light::services::turn_on::fields::brightness::description%]" + }, + "brightness_pct": { + "name": "[%key:component::light::services::turn_on::fields::brightness_pct::name%]", + "description": "[%key:component::light::services::turn_on::fields::brightness_pct::description%]" + }, + "white": { + "name": "[%key:component::light::services::turn_on::fields::white::name%]", + "description": "[%key:component::light::services::turn_on::fields::white::description%]" + }, + "profile": { + "name": "[%key:component::light::services::turn_on::fields::profile::name%]", + "description": "[%key:component::light::services::turn_on::fields::profile::description%]" + }, + "flash": { + "name": "[%key:component::light::services::turn_on::fields::flash::name%]", + "description": "[%key:component::light::services::turn_on::fields::flash::description%]" + }, + "effect": { + "name": "[%key:component::light::services::turn_on::fields::effect::name%]", + "description": "[%key:component::light::services::turn_on::fields::effect::description%]" + } + } + } } } diff --git a/script/hassfest/services.py b/script/hassfest/services.py index a0c629567fac9f..e51e0aeae04ae0 100644 --- a/script/hassfest/services.py +++ b/script/hassfest/services.py @@ -1,6 +1,8 @@ """Validate dependencies.""" from __future__ import annotations +import contextlib +import json import pathlib import re from typing import Any @@ -25,7 +27,7 @@ def exists(value: Any) -> Any: FIELD_SCHEMA = vol.Schema( { - vol.Required("description"): str, + vol.Optional("description"): str, vol.Optional("name"): str, vol.Optional("example"): exists, vol.Optional("default"): exists, @@ -46,7 +48,7 @@ def exists(value: Any) -> Any: SERVICE_SCHEMA = vol.Schema( { - vol.Required("description"): str, + vol.Optional("description"): str, vol.Optional("name"): str, vol.Optional("target"): vol.Any(selector.TargetSelector.CONFIG_SCHEMA, None), vol.Optional("fields"): vol.Schema({str: FIELD_SCHEMA}), @@ -70,7 +72,7 @@ def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) -> bool return False -def validate_services(integration: Integration) -> None: +def validate_services(config: Config, integration: Integration) -> None: """Validate services.""" try: data = load_yaml(str(integration.path / "services.yaml")) @@ -92,15 +94,53 @@ def validate_services(integration: Integration) -> None: return try: - SERVICES_SCHEMA(data) + services = SERVICES_SCHEMA(data) except vol.Invalid as err: integration.add_error( "services", f"Invalid services.yaml: {humanize_error(data, err)}" ) + # Try loading translation strings + if integration.core: + strings_file = integration.path / "strings.json" + else: + # For custom integrations, use the en.json file + strings_file = integration.path / "translations/en.json" + + strings = {} + if strings_file.is_file(): + with contextlib.suppress(ValueError): + strings = json.loads(strings_file.read_text()) + + # For each service in the integration, check if the description if set, + # if not, check if it's in the strings file. If not, add an error. + for service_name, service_schema in services.items(): + if "description" not in service_schema: + try: + strings["services"][service_name]["description"] + except KeyError: + integration.add_error( + "services", + f"Service {service_name} has no description and is not in the translations file", + ) + + # 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 "description" not in field_schema: + try: + strings["services"][service_name]["fields"][field_name][ + "description" + ] + except KeyError: + integration.add_error( + "services", + f"Service {service_name} has a field {field_name} with no description and is not in the translations file", + ) + def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" # check services.yaml is cool for integration in integrations.values(): - validate_services(integration) + validate_services(config, integration) diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 56609e57fd978f..954455abc1ac24 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -325,6 +325,20 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: ), slug_validator=cv.slug, ), + vol.Optional("services"): cv.schema_with_slug_keys( + { + vol.Optional("name"): translation_value_validator, + vol.Required("description"): translation_value_validator, + vol.Optional("fields"): cv.schema_with_slug_keys( + { + vol.Optional("name"): str, + vol.Required("description"): translation_value_validator, + }, + slug_validator=translation_key_validator, + ), + }, + slug_validator=translation_key_validator, + ), } ) From 472f2cf4c2978892d435f7f0540ca94a4b7f33c7 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 6 Jul 2023 11:58:23 +0200 Subject: [PATCH 02/11] Select selector options --- homeassistant/components/light/strings.json | 304 +++++++++++++++++++- script/hassfest/translations.py | 4 + 2 files changed, 306 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index bd6a695999a2e9..5f867d1df60fbd 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -110,7 +110,157 @@ }, "color_name": { "name": "Color name", - "description": "A human readable color name." + "description": "A human readable color name.", + "options": { + "homeassistant": "Home Assistant", + "aliceblue": "Alice blue", + "antiquewhite": "Antique white", + "aqua": "Aqua", + "aquamarine": "Aquamarine", + "azure": "Azure", + "beige": "Beige", + "bisque": "Bisque", + "blanchedalmond": "Blanched almond", + "blue": "Blue", + "blueviolet": "Blue violet", + "brown": "Brown", + "burlywood": "Burlywood", + "cadetblue": "Cadet blue", + "chartreuse": "Chartreuse", + "chocolate": "Chocolate", + "coral": "Coral", + "cornflowerblue": "Cornflower blue", + "cornsilk": "Cornsilk", + "crimson": "Crimson", + "cyan": "Cyan", + "darkblue": "Dark blue", + "darkcyan": "Dark cyan", + "darkgoldenrod": "Dark goldenrod", + "darkgray": "Dark gray", + "darkgreen": "Dark green", + "darkgrey": "Dark grey", + "darkkhaki": "Dark khaki", + "darkmagenta": "Dark magenta", + "darkolivegreen": "Dark olive green", + "darkorange": "Dark orange", + "darkorchid": "Dark orchid", + "darkred": "Dark red", + "darksalmon": "Dark salmon", + "darkseagreen": "Dark sea green", + "darkslateblue": "Dark slate blue", + "darkslategray": "Dark slate gray", + "darkslategrey": "Dark slate grey", + "darkturquoise": "Dark turquoise", + "darkviolet": "Dark violet", + "deeppink": "Deep pink", + "deepskyblue": "Deep sky blue", + "dimgray": "Dim gray", + "dimgrey": "Dim grey", + "dodgerblue": "Dodger blue", + "firebrick": "Fire brick", + "floralwhite": "Floral white", + "forestgreen": "Forest green", + "fuchsia": "Fuchsia", + "gainsboro": "Gainsboro", + "ghostwhite": "Ghost white", + "gold": "Gold", + "goldenrod": "Goldenrod", + "gray": "Gray", + "green": "Green", + "greenyellow": "Green yellow", + "grey": "Grey", + "honeydew": "Honeydew", + "hotpink": "Hot pink", + "indianred": "Indian red", + "indigo": "Indigo", + "ivory": "Ivory", + "khaki": "Khaki", + "lavender": "Lavender", + "lavenderblush": "Lavender blush", + "lawngreen": "Lawn green", + "lemonchiffon": "Lemon chiffon", + "lightblue": "Light blue", + "lightcoral": "Light coral", + "lightcyan": "Light cyan", + "lightgoldenrodyellow": "Light goldenrod yellow", + "lightgray": "Light gray", + "lightgreen": "Light green", + "lightgrey": "Light grey", + "lightpink": "Light pink", + "lightsalmon": "Light salmon", + "lightseagreen": "Light sea green", + "lightskyblue": "Light sky blue", + "lightslategray": "Light slate gray", + "lightslategrey": "Light slate grey", + "lightsteelblue": "Light steel blue", + "lightyellow": "Light yellow", + "lime": "Lime", + "limegreen": "Lime green", + "linen": "Linen", + "magenta": "Magenta", + "maroon": "Maroon", + "mediumaquamarine": "Medium aquamarine", + "mediumblue": "Medium blue", + "mediumorchid": "Medium orchid", + "mediumpurple": "Medium purple", + "mediumseagreen": "Medium sea green", + "mediumslateblue": "Medium slate blue", + "mediumspringgreen": "Medium spring green", + "mediumturquoise": "Medium turquoise", + "mediumvioletred": "Medium violet red", + "midnightblue": "Midnight blue", + "mintcream": "Mint cream", + "mistyrose": "Misty rose", + "moccasin": "Moccasin", + "navajowhite": "Navajo white", + "navy": "Navy", + "navyblue": "Navy blue", + "oldlace": "Old lace", + "olive": "Olive", + "olivedrab": "Olive drab", + "orange": "Orange", + "orangered": "Orange red", + "orchid": "Orchid", + "palegoldenrod": "Pale goldenrod", + "palegreen": "Pale green", + "paleturquoise": "Pale turquoise", + "palevioletred": "Pale violet red", + "papayawhip": "Papaya whip", + "peachpuff": "Peach puff", + "peru": "Peru", + "pink": "Pink", + "plum": "Plum", + "powderblue": "Powder blue", + "purple": "Purple", + "red": "Red", + "rosybrown": "Rosy brown", + "royalblue": "Royal blue", + "saddlebrown": "Saddle brown", + "salmon": "Salmon", + "sandybrown": "Sandy brown", + "seagreen": "Sea green", + "seashell": "Seashell", + "sienna": "Sienna", + "silver": "Silver", + "skyblue": "Sky blue", + "slateblue": "Slate blue", + "slategray": "Slate gray", + "slategrey": "Slate grey", + "snow": "Snow", + "springgreen": "Spring green", + "steelblue": "Steel blue", + "tan": "Tan", + "teal": "Teal", + "thistle": "Thistle", + "tomato": "Tomato", + "turquoise": "Turquoise", + "violet": "Violet", + "wheat": "Wheat", + "white": "White", + "whitesmoke": "White smoke", + "yellow": "Yellow", + "yellowgreen": "Yellow green" + } }, "hs_color": { "name": "Hue/Sat color", @@ -190,7 +340,157 @@ }, "color_name": { "name": "[%key:component::light::services::turn_on::fields::color_name::name%]", - "description": "[%key:component::light::services::turn_on::fields::color_name::description%]" + "description": "[%key:component::light::services::turn_on::fields::color_name::description%]", + "options": { + "homeassistant": "[%key:component::light::services::turn_on::fields::color_name::options::homeassistant%]", + "aliceblue": "[%key:component::light::services::turn_on::fields::color_name::options::aliceblue%]", + "antiquewhite": "[%key:component::light::services::turn_on::fields::color_name::options::antiquewhite%]", + "aqua": "[%key:component::light::services::turn_on::fields::color_name::options::aqua%]", + "aquamarine": "[%key:component::light::services::turn_on::fields::color_name::options::aquamarine%]", + "azure": "[%key:component::light::services::turn_on::fields::color_name::options::azure%]", + "beige": "[%key:component::light::services::turn_on::fields::color_name::options::beige%]", + "bisque": "[%key:component::light::services::turn_on::fields::color_name::options::bisque%]", + "blanchedalmond": "[%key:component::light::services::turn_on::fields::color_name::options::blanchedalmond%]", + "blue": "[%key:component::light::services::turn_on::fields::color_name::options::blue%]", + "blueviolet": "[%key:component::light::services::turn_on::fields::color_name::options::blueviolet%]", + "brown": "[%key:component::light::services::turn_on::fields::color_name::options::brown%]", + "burlywood": "[%key:component::light::services::turn_on::fields::color_name::options::burlywood%]", + "cadetblue": "[%key:component::light::services::turn_on::fields::color_name::options::cadetblue%]", + "chartreuse": "[%key:component::light::services::turn_on::fields::color_name::options::chartreuse%]", + "chocolate": "[%key:component::light::services::turn_on::fields::color_name::options::chocolate%]", + "coral": "[%key:component::light::services::turn_on::fields::color_name::options::coral%]", + "cornflowerblue": "[%key:component::light::services::turn_on::fields::color_name::options::cornflowerblue%]", + "cornsilk": "[%key:component::light::services::turn_on::fields::color_name::options::cornsilk%]", + "crimson": "[%key:component::light::services::turn_on::fields::color_name::options::crimson%]", + "cyan": "[%key:component::light::services::turn_on::fields::color_name::options::cyan%]", + "darkblue": "[%key:component::light::services::turn_on::fields::color_name::options::darkblue%]", + "darkcyan": "[%key:component::light::services::turn_on::fields::color_name::options::darkcyan%]", + "darkgoldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::darkgoldenrod%]", + "darkgray": "[%key:component::light::services::turn_on::fields::color_name::options::darkgray%]", + "darkgreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkgreen%]", + "darkgrey": "[%key:component::light::services::turn_on::fields::color_name::options::darkgrey%]", + "darkkhaki": "[%key:component::light::services::turn_on::fields::color_name::options::darkkhaki%]", + "darkmagenta": "[%key:component::light::services::turn_on::fields::color_name::options::darkmagenta%]", + "darkolivegreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkolivegreen%]", + "darkorange": "[%key:component::light::services::turn_on::fields::color_name::options::darkorange%]", + "darkorchid": "[%key:component::light::services::turn_on::fields::color_name::options::darkorchid%]", + "darkred": "[%key:component::light::services::turn_on::fields::color_name::options::darkred%]", + "darksalmon": "[%key:component::light::services::turn_on::fields::color_name::options::darksalmon%]", + "darkseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkseagreen%]", + "darkslateblue": "[%key:component::light::services::turn_on::fields::color_name::options::darkslateblue%]", + "darkslategray": "[%key:component::light::services::turn_on::fields::color_name::options::darkslategray%]", + "darkslategrey": "[%key:component::light::services::turn_on::fields::color_name::options::darkslategrey%]", + "darkturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::darkturquoise%]", + "darkviolet": "[%key:component::light::services::turn_on::fields::color_name::options::darkviolet%]", + "deeppink": "[%key:component::light::services::turn_on::fields::color_name::options::deeppink%]", + "deepskyblue": "[%key:component::light::services::turn_on::fields::color_name::options::deepskyblue%]", + "dimgray": "[%key:component::light::services::turn_on::fields::color_name::options::dimgray%]", + "dimgrey": "[%key:component::light::services::turn_on::fields::color_name::options::dimgrey%]", + "dodgerblue": "[%key:component::light::services::turn_on::fields::color_name::options::dodgerblue%]", + "firebrick": "[%key:component::light::services::turn_on::fields::color_name::options::firebrick%]", + "floralwhite": "[%key:component::light::services::turn_on::fields::color_name::options::floralwhite%]", + "forestgreen": "[%key:component::light::services::turn_on::fields::color_name::options::forestgreen%]", + "fuchsia": "[%key:component::light::services::turn_on::fields::color_name::options::fuchsia%]", + "gainsboro": "[%key:component::light::services::turn_on::fields::color_name::options::gainsboro%]", + "ghostwhite": "[%key:component::light::services::turn_on::fields::color_name::options::ghostwhite%]", + "gold": "[%key:component::light::services::turn_on::fields::color_name::options::gold%]", + "goldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::goldenrod%]", + "gray": "[%key:component::light::services::turn_on::fields::color_name::options::gray%]", + "green": "[%key:component::light::services::turn_on::fields::color_name::options::green%]", + "greenyellow": "[%key:component::light::services::turn_on::fields::color_name::options::greenyellow%]", + "grey": "[%key:component::light::services::turn_on::fields::color_name::options::grey%]", + "honeydew": "[%key:component::light::services::turn_on::fields::color_name::options::honeydew%]", + "hotpink": "[%key:component::light::services::turn_on::fields::color_name::options::hotpink%]", + "indianred": "[%key:component::light::services::turn_on::fields::color_name::options::indianred%]", + "indigo": "[%key:component::light::services::turn_on::fields::color_name::options::indigo%]", + "ivory": "[%key:component::light::services::turn_on::fields::color_name::options::ivory%]", + "khaki": "[%key:component::light::services::turn_on::fields::color_name::options::khaki%]", + "lavender": "[%key:component::light::services::turn_on::fields::color_name::options::lavender%]", + "lavenderblush": "[%key:component::light::services::turn_on::fields::color_name::options::lavenderblush%]", + "lawngreen": "[%key:component::light::services::turn_on::fields::color_name::options::lawngreen%]", + "lemonchiffon": "[%key:component::light::services::turn_on::fields::color_name::options::lemonchiffon%]", + "lightblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightblue%]", + "lightcoral": "[%key:component::light::services::turn_on::fields::color_name::options::lightcoral%]", + "lightcyan": "[%key:component::light::services::turn_on::fields::color_name::options::lightcyan%]", + "lightgoldenrodyellow": "[%key:component::light::services::turn_on::fields::color_name::options::lightgoldenrodyellow%]", + "lightgray": "[%key:component::light::services::turn_on::fields::color_name::options::lightgray%]", + "lightgreen": "[%key:component::light::services::turn_on::fields::color_name::options::lightgreen%]", + "lightgrey": "[%key:component::light::services::turn_on::fields::color_name::options::lightgrey%]", + "lightpink": "[%key:component::light::services::turn_on::fields::color_name::options::lightpink%]", + "lightsalmon": "[%key:component::light::services::turn_on::fields::color_name::options::lightsalmon%]", + "lightseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::lightseagreen%]", + "lightskyblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightskyblue%]", + "lightslategray": "[%key:component::light::services::turn_on::fields::color_name::options::lightslategray%]", + "lightslategrey": "[%key:component::light::services::turn_on::fields::color_name::options::lightslategrey%]", + "lightsteelblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightsteelblue%]", + "lightyellow": "[%key:component::light::services::turn_on::fields::color_name::options::lightyellow%]", + "lime": "[%key:component::light::services::turn_on::fields::color_name::options::lime%]", + "limegreen": "[%key:component::light::services::turn_on::fields::color_name::options::limegreen%]", + "linen": "[%key:component::light::services::turn_on::fields::color_name::options::linen%]", + "magenta": "[%key:component::light::services::turn_on::fields::color_name::options::magenta%]", + "maroon": "[%key:component::light::services::turn_on::fields::color_name::options::maroon%]", + "mediumaquamarine": "[%key:component::light::services::turn_on::fields::color_name::options::mediumaquamarine%]", + "mediumblue": "[%key:component::light::services::turn_on::fields::color_name::options::mediumblue%]", + "mediumorchid": "[%key:component::light::services::turn_on::fields::color_name::options::mediumorchid%]", + "mediumpurple": "[%key:component::light::services::turn_on::fields::color_name::options::mediumpurple%]", + "mediumseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::mediumseagreen%]", + "mediumslateblue": "[%key:component::light::services::turn_on::fields::color_name::options::mediumslateblue%]", + "mediumspringgreen": "[%key:component::light::services::turn_on::fields::color_name::options::mediumspringgreen%]", + "mediumturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::mediumturquoise%]", + "mediumvioletred": "[%key:component::light::services::turn_on::fields::color_name::options::mediumvioletred%]", + "midnightblue": "[%key:component::light::services::turn_on::fields::color_name::options::midnightblue%]", + "mintcream": "[%key:component::light::services::turn_on::fields::color_name::options::mintcream%]", + "mistyrose": "[%key:component::light::services::turn_on::fields::color_name::options::mistyrose%]", + "moccasin": "[%key:component::light::services::turn_on::fields::color_name::options::moccasin%]", + "navajowhite": "[%key:component::light::services::turn_on::fields::color_name::options::navajowhite%]", + "navy": "[%key:component::light::services::turn_on::fields::color_name::options::navy%]", + "navyblue": "[%key:component::light::services::turn_on::fields::color_name::options::navyblue%]", + "oldlace": "[%key:component::light::services::turn_on::fields::color_name::options::oldlace%]", + "olive": "[%key:component::light::services::turn_on::fields::color_name::options::olive%]", + "olivedrab": "[%key:component::light::services::turn_on::fields::color_name::options::olivedrab%]", + "orange": "[%key:component::light::services::turn_on::fields::color_name::options::orange%]", + "orangered": "[%key:component::light::services::turn_on::fields::color_name::options::orangered%]", + "orchid": "[%key:component::light::services::turn_on::fields::color_name::options::orchid%]", + "palegoldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::palegoldenrod%]", + "palegreen": "[%key:component::light::services::turn_on::fields::color_name::options::palegreen%]", + "paleturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::paleturquoise%]", + "palevioletred": "[%key:component::light::services::turn_on::fields::color_name::options::palevioletred%]", + "papayawhip": "[%key:component::light::services::turn_on::fields::color_name::options::papayawhip%]", + "peachpuff": "[%key:component::light::services::turn_on::fields::color_name::options::peachpuff%]", + "peru": "[%key:component::light::services::turn_on::fields::color_name::options::peru%]", + "pink": "[%key:component::light::services::turn_on::fields::color_name::options::pink%]", + "plum": "[%key:component::light::services::turn_on::fields::color_name::options::plum%]", + "powderblue": "[%key:component::light::services::turn_on::fields::color_name::options::powderblue%]", + "purple": "[%key:component::light::services::turn_on::fields::color_name::options::purple%]", + "red": "[%key:component::light::services::turn_on::fields::color_name::options::red%]", + "rosybrown": "[%key:component::light::services::turn_on::fields::color_name::options::rosybrown%]", + "royalblue": "[%key:component::light::services::turn_on::fields::color_name::options::royalblue%]", + "saddlebrown": "[%key:component::light::services::turn_on::fields::color_name::options::saddlebrown%]", + "salmon": "[%key:component::light::services::turn_on::fields::color_name::options::salmon%]", + "sandybrown": "[%key:component::light::services::turn_on::fields::color_name::options::sandybrown%]", + "seagreen": "[%key:component::light::services::turn_on::fields::color_name::options::seagreen%]", + "seashell": "[%key:component::light::services::turn_on::fields::color_name::options::seashell%]", + "sienna": "[%key:component::light::services::turn_on::fields::color_name::options::sienna%]", + "silver": "[%key:component::light::services::turn_on::fields::color_name::options::silver%]", + "skyblue": "[%key:component::light::services::turn_on::fields::color_name::options::skyblue%]", + "slateblue": "[%key:component::light::services::turn_on::fields::color_name::options::slateblue%]", + "slategray": "[%key:component::light::services::turn_on::fields::color_name::options::slategray%]", + "slategrey": "[%key:component::light::services::turn_on::fields::color_name::options::slategrey%]", + "snow": "[%key:component::light::services::turn_on::fields::color_name::options::snow%]", + "springgreen": "[%key:component::light::services::turn_on::fields::color_name::options::springgreen%]", + "steelblue": "[%key:component::light::services::turn_on::fields::color_name::options::steelblue%]", + "tan": "[%key:component::light::services::turn_on::fields::color_name::options::tan%]", + "teal": "[%key:component::light::services::turn_on::fields::color_name::options::teal%]", + "thistle": "[%key:component::light::services::turn_on::fields::color_name::options::thistle%]", + "tomato": "[%key:component::light::services::turn_on::fields::color_name::options::tomato%]", + "turquoise": "[%key:component::light::services::turn_on::fields::color_name::options::turquoise%]", + "violet": "[%key:component::light::services::turn_on::fields::color_name::options::violet%]", + "wheat": "[%key:component::light::services::turn_on::fields::color_name::options::wheat%]", + "white": "[%key:component::light::services::turn_on::fields::color_name::options::white%]", + "whitesmoke": "[%key:component::light::services::turn_on::fields::color_name::options::whitesmoke%]", + "yellow": "[%key:component::light::services::turn_on::fields::color_name::options::yellow%]", + "yellowgreen": "[%key:component::light::services::turn_on::fields::color_name::options::yellowgreen%]" + } }, "hs_color": { "name": "[%key:component::light::services::turn_on::fields::hs_color::name%]", diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 954455abc1ac24..db49b40a473ea6 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -333,6 +333,10 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: { vol.Optional("name"): str, vol.Required("description"): translation_value_validator, + vol.Optional("options"): cv.schema_with_slug_keys( + translation_value_validator, + slug_validator=translation_key_validator, + ), }, slug_validator=translation_key_validator, ), From 1d1916611fb9a0e25c3e8f83ec8aaae2725af9df Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 6 Jul 2023 16:23:13 +0200 Subject: [PATCH 03/11] Re-use existing selector translations functionality --- homeassistant/components/light/services.yaml | 2 + homeassistant/components/light/strings.json | 458 +++++++------------ script/hassfest/services.py | 13 + script/hassfest/translations.py | 4 - 4 files changed, 171 insertions(+), 306 deletions(-) diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index ad14162f391264..1ba204e5eda900 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -63,6 +63,7 @@ turn_on: advanced: true selector: select: + translation_key: color_name options: - "homeassistant" - "aliceblue" @@ -434,6 +435,7 @@ toggle: advanced: true selector: select: + translation_key: color_name options: - "homeassistant" - "aliceblue" diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index 5f867d1df60fbd..7043c9e81a800d 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -87,6 +87,160 @@ } } }, + "selector": { + "color_name": { + "options": { + "homeassistant": "Home Assistant", + "aliceblue": "Alice blue", + "antiquewhite": "Antique white", + "aqua": "Aqua", + "aquamarine": "Aquamarine", + "azure": "Azure", + "beige": "Beige", + "bisque": "Bisque", + "blanchedalmond": "Blanched almond", + "blue": "Blue", + "blueviolet": "Blue violet", + "brown": "Brown", + "burlywood": "Burlywood", + "cadetblue": "Cadet blue", + "chartreuse": "Chartreuse", + "chocolate": "Chocolate", + "coral": "Coral", + "cornflowerblue": "Cornflower blue", + "cornsilk": "Cornsilk", + "crimson": "Crimson", + "cyan": "Cyan", + "darkblue": "Dark blue", + "darkcyan": "Dark cyan", + "darkgoldenrod": "Dark goldenrod", + "darkgray": "Dark gray", + "darkgreen": "Dark green", + "darkgrey": "Dark grey", + "darkkhaki": "Dark khaki", + "darkmagenta": "Dark magenta", + "darkolivegreen": "Dark olive green", + "darkorange": "Dark orange", + "darkorchid": "Dark orchid", + "darkred": "Dark red", + "darksalmon": "Dark salmon", + "darkseagreen": "Dark sea green", + "darkslateblue": "Dark slate blue", + "darkslategray": "Dark slate gray", + "darkslategrey": "Dark slate grey", + "darkturquoise": "Dark turquoise", + "darkviolet": "Dark violet", + "deeppink": "Deep pink", + "deepskyblue": "Deep sky blue", + "dimgray": "Dim gray", + "dimgrey": "Dim grey", + "dodgerblue": "Dodger blue", + "firebrick": "Fire brick", + "floralwhite": "Floral white", + "forestgreen": "Forest green", + "fuchsia": "Fuchsia", + "gainsboro": "Gainsboro", + "ghostwhite": "Ghost white", + "gold": "Gold", + "goldenrod": "Goldenrod", + "gray": "Gray", + "green": "Green", + "greenyellow": "Green yellow", + "grey": "Grey", + "honeydew": "Honeydew", + "hotpink": "Hot pink", + "indianred": "Indian red", + "indigo": "Indigo", + "ivory": "Ivory", + "khaki": "Khaki", + "lavender": "Lavender", + "lavenderblush": "Lavender blush", + "lawngreen": "Lawn green", + "lemonchiffon": "Lemon chiffon", + "lightblue": "Light blue", + "lightcoral": "Light coral", + "lightcyan": "Light cyan", + "lightgoldenrodyellow": "Light goldenrod yellow", + "lightgray": "Light gray", + "lightgreen": "Light green", + "lightgrey": "Light grey", + "lightpink": "Light pink", + "lightsalmon": "Light salmon", + "lightseagreen": "Light sea green", + "lightskyblue": "Light sky blue", + "lightslategray": "Light slate gray", + "lightslategrey": "Light slate grey", + "lightsteelblue": "Light steel blue", + "lightyellow": "Light yellow", + "lime": "Lime", + "limegreen": "Lime green", + "linen": "Linen", + "magenta": "Magenta", + "maroon": "Maroon", + "mediumaquamarine": "Medium aquamarine", + "mediumblue": "Medium blue", + "mediumorchid": "Medium orchid", + "mediumpurple": "Medium purple", + "mediumseagreen": "Medium sea green", + "mediumslateblue": "Medium slate blue", + "mediumspringgreen": "Medium spring green", + "mediumturquoise": "Medium turquoise", + "mediumvioletred": "Medium violet red", + "midnightblue": "Midnight blue", + "mintcream": "Mint cream", + "mistyrose": "Misty rose", + "moccasin": "Moccasin", + "navajowhite": "Navajo white", + "navy": "Navy", + "navyblue": "Navy blue", + "oldlace": "Old lace", + "olive": "Olive", + "olivedrab": "Olive drab", + "orange": "Orange", + "orangered": "Orange red", + "orchid": "Orchid", + "palegoldenrod": "Pale goldenrod", + "palegreen": "Pale green", + "paleturquoise": "Pale turquoise", + "palevioletred": "Pale violet red", + "papayawhip": "Papaya whip", + "peachpuff": "Peach puff", + "peru": "Peru", + "pink": "Pink", + "plum": "Plum", + "powderblue": "Powder blue", + "purple": "Purple", + "red": "Red", + "rosybrown": "Rosy brown", + "royalblue": "Royal blue", + "saddlebrown": "Saddle brown", + "salmon": "Salmon", + "sandybrown": "Sandy brown", + "seagreen": "Sea green", + "seashell": "Seashell", + "sienna": "Sienna", + "silver": "Silver", + "skyblue": "Sky blue", + "slateblue": "Slate blue", + "slategray": "Slate gray", + "slategrey": "Slate grey", + "snow": "Snow", + "springgreen": "Spring green", + "steelblue": "Steel blue", + "tan": "Tan", + "teal": "Teal", + "thistle": "Thistle", + "tomato": "Tomato", + "turquoise": "Turquoise", + "violet": "Violet", + "wheat": "Wheat", + "white": "White", + "whitesmoke": "White smoke", + "yellow": "Yellow", + "yellowgreen": "Yellow green" + } + } + }, "services": { "turn_on": { "name": "Turn on", @@ -110,157 +264,7 @@ }, "color_name": { "name": "Color name", - "description": "A human readable color name.", - "options": { - "homeassistant": "Home Assistant", - "aliceblue": "Alice blue", - "antiquewhite": "Antique white", - "aqua": "Aqua", - "aquamarine": "Aquamarine", - "azure": "Azure", - "beige": "Beige", - "bisque": "Bisque", - "blanchedalmond": "Blanched almond", - "blue": "Blue", - "blueviolet": "Blue violet", - "brown": "Brown", - "burlywood": "Burlywood", - "cadetblue": "Cadet blue", - "chartreuse": "Chartreuse", - "chocolate": "Chocolate", - "coral": "Coral", - "cornflowerblue": "Cornflower blue", - "cornsilk": "Cornsilk", - "crimson": "Crimson", - "cyan": "Cyan", - "darkblue": "Dark blue", - "darkcyan": "Dark cyan", - "darkgoldenrod": "Dark goldenrod", - "darkgray": "Dark gray", - "darkgreen": "Dark green", - "darkgrey": "Dark grey", - "darkkhaki": "Dark khaki", - "darkmagenta": "Dark magenta", - "darkolivegreen": "Dark olive green", - "darkorange": "Dark orange", - "darkorchid": "Dark orchid", - "darkred": "Dark red", - "darksalmon": "Dark salmon", - "darkseagreen": "Dark sea green", - "darkslateblue": "Dark slate blue", - "darkslategray": "Dark slate gray", - "darkslategrey": "Dark slate grey", - "darkturquoise": "Dark turquoise", - "darkviolet": "Dark violet", - "deeppink": "Deep pink", - "deepskyblue": "Deep sky blue", - "dimgray": "Dim gray", - "dimgrey": "Dim grey", - "dodgerblue": "Dodger blue", - "firebrick": "Fire brick", - "floralwhite": "Floral white", - "forestgreen": "Forest green", - "fuchsia": "Fuchsia", - "gainsboro": "Gainsboro", - "ghostwhite": "Ghost white", - "gold": "Gold", - "goldenrod": "Goldenrod", - "gray": "Gray", - "green": "Green", - "greenyellow": "Green yellow", - "grey": "Grey", - "honeydew": "Honeydew", - "hotpink": "Hot pink", - "indianred": "Indian red", - "indigo": "Indigo", - "ivory": "Ivory", - "khaki": "Khaki", - "lavender": "Lavender", - "lavenderblush": "Lavender blush", - "lawngreen": "Lawn green", - "lemonchiffon": "Lemon chiffon", - "lightblue": "Light blue", - "lightcoral": "Light coral", - "lightcyan": "Light cyan", - "lightgoldenrodyellow": "Light goldenrod yellow", - "lightgray": "Light gray", - "lightgreen": "Light green", - "lightgrey": "Light grey", - "lightpink": "Light pink", - "lightsalmon": "Light salmon", - "lightseagreen": "Light sea green", - "lightskyblue": "Light sky blue", - "lightslategray": "Light slate gray", - "lightslategrey": "Light slate grey", - "lightsteelblue": "Light steel blue", - "lightyellow": "Light yellow", - "lime": "Lime", - "limegreen": "Lime green", - "linen": "Linen", - "magenta": "Magenta", - "maroon": "Maroon", - "mediumaquamarine": "Medium aquamarine", - "mediumblue": "Medium blue", - "mediumorchid": "Medium orchid", - "mediumpurple": "Medium purple", - "mediumseagreen": "Medium sea green", - "mediumslateblue": "Medium slate blue", - "mediumspringgreen": "Medium spring green", - "mediumturquoise": "Medium turquoise", - "mediumvioletred": "Medium violet red", - "midnightblue": "Midnight blue", - "mintcream": "Mint cream", - "mistyrose": "Misty rose", - "moccasin": "Moccasin", - "navajowhite": "Navajo white", - "navy": "Navy", - "navyblue": "Navy blue", - "oldlace": "Old lace", - "olive": "Olive", - "olivedrab": "Olive drab", - "orange": "Orange", - "orangered": "Orange red", - "orchid": "Orchid", - "palegoldenrod": "Pale goldenrod", - "palegreen": "Pale green", - "paleturquoise": "Pale turquoise", - "palevioletred": "Pale violet red", - "papayawhip": "Papaya whip", - "peachpuff": "Peach puff", - "peru": "Peru", - "pink": "Pink", - "plum": "Plum", - "powderblue": "Powder blue", - "purple": "Purple", - "red": "Red", - "rosybrown": "Rosy brown", - "royalblue": "Royal blue", - "saddlebrown": "Saddle brown", - "salmon": "Salmon", - "sandybrown": "Sandy brown", - "seagreen": "Sea green", - "seashell": "Seashell", - "sienna": "Sienna", - "silver": "Silver", - "skyblue": "Sky blue", - "slateblue": "Slate blue", - "slategray": "Slate gray", - "slategrey": "Slate grey", - "snow": "Snow", - "springgreen": "Spring green", - "steelblue": "Steel blue", - "tan": "Tan", - "teal": "Teal", - "thistle": "Thistle", - "tomato": "Tomato", - "turquoise": "Turquoise", - "violet": "Violet", - "wheat": "Wheat", - "white": "White", - "whitesmoke": "White smoke", - "yellow": "Yellow", - "yellowgreen": "Yellow green" - } + "description": "A human readable color name." }, "hs_color": { "name": "Hue/Sat color", @@ -340,157 +344,7 @@ }, "color_name": { "name": "[%key:component::light::services::turn_on::fields::color_name::name%]", - "description": "[%key:component::light::services::turn_on::fields::color_name::description%]", - "options": { - "homeassistant": "[%key:component::light::services::turn_on::fields::color_name::options::homeassistant%]", - "aliceblue": "[%key:component::light::services::turn_on::fields::color_name::options::aliceblue%]", - "antiquewhite": "[%key:component::light::services::turn_on::fields::color_name::options::antiquewhite%]", - "aqua": "[%key:component::light::services::turn_on::fields::color_name::options::aqua%]", - "aquamarine": "[%key:component::light::services::turn_on::fields::color_name::options::aquamarine%]", - "azure": "[%key:component::light::services::turn_on::fields::color_name::options::azure%]", - "beige": "[%key:component::light::services::turn_on::fields::color_name::options::beige%]", - "bisque": "[%key:component::light::services::turn_on::fields::color_name::options::bisque%]", - "blanchedalmond": "[%key:component::light::services::turn_on::fields::color_name::options::blanchedalmond%]", - "blue": "[%key:component::light::services::turn_on::fields::color_name::options::blue%]", - "blueviolet": "[%key:component::light::services::turn_on::fields::color_name::options::blueviolet%]", - "brown": "[%key:component::light::services::turn_on::fields::color_name::options::brown%]", - "burlywood": "[%key:component::light::services::turn_on::fields::color_name::options::burlywood%]", - "cadetblue": "[%key:component::light::services::turn_on::fields::color_name::options::cadetblue%]", - "chartreuse": "[%key:component::light::services::turn_on::fields::color_name::options::chartreuse%]", - "chocolate": "[%key:component::light::services::turn_on::fields::color_name::options::chocolate%]", - "coral": "[%key:component::light::services::turn_on::fields::color_name::options::coral%]", - "cornflowerblue": "[%key:component::light::services::turn_on::fields::color_name::options::cornflowerblue%]", - "cornsilk": "[%key:component::light::services::turn_on::fields::color_name::options::cornsilk%]", - "crimson": "[%key:component::light::services::turn_on::fields::color_name::options::crimson%]", - "cyan": "[%key:component::light::services::turn_on::fields::color_name::options::cyan%]", - "darkblue": "[%key:component::light::services::turn_on::fields::color_name::options::darkblue%]", - "darkcyan": "[%key:component::light::services::turn_on::fields::color_name::options::darkcyan%]", - "darkgoldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::darkgoldenrod%]", - "darkgray": "[%key:component::light::services::turn_on::fields::color_name::options::darkgray%]", - "darkgreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkgreen%]", - "darkgrey": "[%key:component::light::services::turn_on::fields::color_name::options::darkgrey%]", - "darkkhaki": "[%key:component::light::services::turn_on::fields::color_name::options::darkkhaki%]", - "darkmagenta": "[%key:component::light::services::turn_on::fields::color_name::options::darkmagenta%]", - "darkolivegreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkolivegreen%]", - "darkorange": "[%key:component::light::services::turn_on::fields::color_name::options::darkorange%]", - "darkorchid": "[%key:component::light::services::turn_on::fields::color_name::options::darkorchid%]", - "darkred": "[%key:component::light::services::turn_on::fields::color_name::options::darkred%]", - "darksalmon": "[%key:component::light::services::turn_on::fields::color_name::options::darksalmon%]", - "darkseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::darkseagreen%]", - "darkslateblue": "[%key:component::light::services::turn_on::fields::color_name::options::darkslateblue%]", - "darkslategray": "[%key:component::light::services::turn_on::fields::color_name::options::darkslategray%]", - "darkslategrey": "[%key:component::light::services::turn_on::fields::color_name::options::darkslategrey%]", - "darkturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::darkturquoise%]", - "darkviolet": "[%key:component::light::services::turn_on::fields::color_name::options::darkviolet%]", - "deeppink": "[%key:component::light::services::turn_on::fields::color_name::options::deeppink%]", - "deepskyblue": "[%key:component::light::services::turn_on::fields::color_name::options::deepskyblue%]", - "dimgray": "[%key:component::light::services::turn_on::fields::color_name::options::dimgray%]", - "dimgrey": "[%key:component::light::services::turn_on::fields::color_name::options::dimgrey%]", - "dodgerblue": "[%key:component::light::services::turn_on::fields::color_name::options::dodgerblue%]", - "firebrick": "[%key:component::light::services::turn_on::fields::color_name::options::firebrick%]", - "floralwhite": "[%key:component::light::services::turn_on::fields::color_name::options::floralwhite%]", - "forestgreen": "[%key:component::light::services::turn_on::fields::color_name::options::forestgreen%]", - "fuchsia": "[%key:component::light::services::turn_on::fields::color_name::options::fuchsia%]", - "gainsboro": "[%key:component::light::services::turn_on::fields::color_name::options::gainsboro%]", - "ghostwhite": "[%key:component::light::services::turn_on::fields::color_name::options::ghostwhite%]", - "gold": "[%key:component::light::services::turn_on::fields::color_name::options::gold%]", - "goldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::goldenrod%]", - "gray": "[%key:component::light::services::turn_on::fields::color_name::options::gray%]", - "green": "[%key:component::light::services::turn_on::fields::color_name::options::green%]", - "greenyellow": "[%key:component::light::services::turn_on::fields::color_name::options::greenyellow%]", - "grey": "[%key:component::light::services::turn_on::fields::color_name::options::grey%]", - "honeydew": "[%key:component::light::services::turn_on::fields::color_name::options::honeydew%]", - "hotpink": "[%key:component::light::services::turn_on::fields::color_name::options::hotpink%]", - "indianred": "[%key:component::light::services::turn_on::fields::color_name::options::indianred%]", - "indigo": "[%key:component::light::services::turn_on::fields::color_name::options::indigo%]", - "ivory": "[%key:component::light::services::turn_on::fields::color_name::options::ivory%]", - "khaki": "[%key:component::light::services::turn_on::fields::color_name::options::khaki%]", - "lavender": "[%key:component::light::services::turn_on::fields::color_name::options::lavender%]", - "lavenderblush": "[%key:component::light::services::turn_on::fields::color_name::options::lavenderblush%]", - "lawngreen": "[%key:component::light::services::turn_on::fields::color_name::options::lawngreen%]", - "lemonchiffon": "[%key:component::light::services::turn_on::fields::color_name::options::lemonchiffon%]", - "lightblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightblue%]", - "lightcoral": "[%key:component::light::services::turn_on::fields::color_name::options::lightcoral%]", - "lightcyan": "[%key:component::light::services::turn_on::fields::color_name::options::lightcyan%]", - "lightgoldenrodyellow": "[%key:component::light::services::turn_on::fields::color_name::options::lightgoldenrodyellow%]", - "lightgray": "[%key:component::light::services::turn_on::fields::color_name::options::lightgray%]", - "lightgreen": "[%key:component::light::services::turn_on::fields::color_name::options::lightgreen%]", - "lightgrey": "[%key:component::light::services::turn_on::fields::color_name::options::lightgrey%]", - "lightpink": "[%key:component::light::services::turn_on::fields::color_name::options::lightpink%]", - "lightsalmon": "[%key:component::light::services::turn_on::fields::color_name::options::lightsalmon%]", - "lightseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::lightseagreen%]", - "lightskyblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightskyblue%]", - "lightslategray": "[%key:component::light::services::turn_on::fields::color_name::options::lightslategray%]", - "lightslategrey": "[%key:component::light::services::turn_on::fields::color_name::options::lightslategrey%]", - "lightsteelblue": "[%key:component::light::services::turn_on::fields::color_name::options::lightsteelblue%]", - "lightyellow": "[%key:component::light::services::turn_on::fields::color_name::options::lightyellow%]", - "lime": "[%key:component::light::services::turn_on::fields::color_name::options::lime%]", - "limegreen": "[%key:component::light::services::turn_on::fields::color_name::options::limegreen%]", - "linen": "[%key:component::light::services::turn_on::fields::color_name::options::linen%]", - "magenta": "[%key:component::light::services::turn_on::fields::color_name::options::magenta%]", - "maroon": "[%key:component::light::services::turn_on::fields::color_name::options::maroon%]", - "mediumaquamarine": "[%key:component::light::services::turn_on::fields::color_name::options::mediumaquamarine%]", - "mediumblue": "[%key:component::light::services::turn_on::fields::color_name::options::mediumblue%]", - "mediumorchid": "[%key:component::light::services::turn_on::fields::color_name::options::mediumorchid%]", - "mediumpurple": "[%key:component::light::services::turn_on::fields::color_name::options::mediumpurple%]", - "mediumseagreen": "[%key:component::light::services::turn_on::fields::color_name::options::mediumseagreen%]", - "mediumslateblue": "[%key:component::light::services::turn_on::fields::color_name::options::mediumslateblue%]", - "mediumspringgreen": "[%key:component::light::services::turn_on::fields::color_name::options::mediumspringgreen%]", - "mediumturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::mediumturquoise%]", - "mediumvioletred": "[%key:component::light::services::turn_on::fields::color_name::options::mediumvioletred%]", - "midnightblue": "[%key:component::light::services::turn_on::fields::color_name::options::midnightblue%]", - "mintcream": "[%key:component::light::services::turn_on::fields::color_name::options::mintcream%]", - "mistyrose": "[%key:component::light::services::turn_on::fields::color_name::options::mistyrose%]", - "moccasin": "[%key:component::light::services::turn_on::fields::color_name::options::moccasin%]", - "navajowhite": "[%key:component::light::services::turn_on::fields::color_name::options::navajowhite%]", - "navy": "[%key:component::light::services::turn_on::fields::color_name::options::navy%]", - "navyblue": "[%key:component::light::services::turn_on::fields::color_name::options::navyblue%]", - "oldlace": "[%key:component::light::services::turn_on::fields::color_name::options::oldlace%]", - "olive": "[%key:component::light::services::turn_on::fields::color_name::options::olive%]", - "olivedrab": "[%key:component::light::services::turn_on::fields::color_name::options::olivedrab%]", - "orange": "[%key:component::light::services::turn_on::fields::color_name::options::orange%]", - "orangered": "[%key:component::light::services::turn_on::fields::color_name::options::orangered%]", - "orchid": "[%key:component::light::services::turn_on::fields::color_name::options::orchid%]", - "palegoldenrod": "[%key:component::light::services::turn_on::fields::color_name::options::palegoldenrod%]", - "palegreen": "[%key:component::light::services::turn_on::fields::color_name::options::palegreen%]", - "paleturquoise": "[%key:component::light::services::turn_on::fields::color_name::options::paleturquoise%]", - "palevioletred": "[%key:component::light::services::turn_on::fields::color_name::options::palevioletred%]", - "papayawhip": "[%key:component::light::services::turn_on::fields::color_name::options::papayawhip%]", - "peachpuff": "[%key:component::light::services::turn_on::fields::color_name::options::peachpuff%]", - "peru": "[%key:component::light::services::turn_on::fields::color_name::options::peru%]", - "pink": "[%key:component::light::services::turn_on::fields::color_name::options::pink%]", - "plum": "[%key:component::light::services::turn_on::fields::color_name::options::plum%]", - "powderblue": "[%key:component::light::services::turn_on::fields::color_name::options::powderblue%]", - "purple": "[%key:component::light::services::turn_on::fields::color_name::options::purple%]", - "red": "[%key:component::light::services::turn_on::fields::color_name::options::red%]", - "rosybrown": "[%key:component::light::services::turn_on::fields::color_name::options::rosybrown%]", - "royalblue": "[%key:component::light::services::turn_on::fields::color_name::options::royalblue%]", - "saddlebrown": "[%key:component::light::services::turn_on::fields::color_name::options::saddlebrown%]", - "salmon": "[%key:component::light::services::turn_on::fields::color_name::options::salmon%]", - "sandybrown": "[%key:component::light::services::turn_on::fields::color_name::options::sandybrown%]", - "seagreen": "[%key:component::light::services::turn_on::fields::color_name::options::seagreen%]", - "seashell": "[%key:component::light::services::turn_on::fields::color_name::options::seashell%]", - "sienna": "[%key:component::light::services::turn_on::fields::color_name::options::sienna%]", - "silver": "[%key:component::light::services::turn_on::fields::color_name::options::silver%]", - "skyblue": "[%key:component::light::services::turn_on::fields::color_name::options::skyblue%]", - "slateblue": "[%key:component::light::services::turn_on::fields::color_name::options::slateblue%]", - "slategray": "[%key:component::light::services::turn_on::fields::color_name::options::slategray%]", - "slategrey": "[%key:component::light::services::turn_on::fields::color_name::options::slategrey%]", - "snow": "[%key:component::light::services::turn_on::fields::color_name::options::snow%]", - "springgreen": "[%key:component::light::services::turn_on::fields::color_name::options::springgreen%]", - "steelblue": "[%key:component::light::services::turn_on::fields::color_name::options::steelblue%]", - "tan": "[%key:component::light::services::turn_on::fields::color_name::options::tan%]", - "teal": "[%key:component::light::services::turn_on::fields::color_name::options::teal%]", - "thistle": "[%key:component::light::services::turn_on::fields::color_name::options::thistle%]", - "tomato": "[%key:component::light::services::turn_on::fields::color_name::options::tomato%]", - "turquoise": "[%key:component::light::services::turn_on::fields::color_name::options::turquoise%]", - "violet": "[%key:component::light::services::turn_on::fields::color_name::options::violet%]", - "wheat": "[%key:component::light::services::turn_on::fields::color_name::options::wheat%]", - "white": "[%key:component::light::services::turn_on::fields::color_name::options::white%]", - "whitesmoke": "[%key:component::light::services::turn_on::fields::color_name::options::whitesmoke%]", - "yellow": "[%key:component::light::services::turn_on::fields::color_name::options::yellow%]", - "yellowgreen": "[%key:component::light::services::turn_on::fields::color_name::options::yellowgreen%]" - } + "description": "[%key:component::light::services::turn_on::fields::color_name::description%]" }, "hs_color": { "name": "[%key:component::light::services::turn_on::fields::hs_color::name%]", diff --git a/script/hassfest/services.py b/script/hassfest/services.py index e51e0aeae04ae0..96e22768c4bf07 100644 --- a/script/hassfest/services.py +++ b/script/hassfest/services.py @@ -138,6 +138,19 @@ def validate_services(config: Config, integration: Integration) -> None: f"Service {service_name} has a field {field_name} with no description and is not in the translations file", ) + if "selector" in field_schema: + with contextlib.suppress(KeyError): + translation_key = field_schema["selector"]["select"][ + "translation_key" + ] + try: + strings["selector"][translation_key] + except KeyError: + integration.add_error( + "services", + 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", + ) + def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index db49b40a473ea6..954455abc1ac24 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -333,10 +333,6 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: { vol.Optional("name"): str, vol.Required("description"): translation_value_validator, - vol.Optional("options"): cv.schema_with_slug_keys( - translation_value_validator, - slug_validator=translation_key_validator, - ), }, slug_validator=translation_key_validator, ), From 23c8c23d45f79c8364e127cc46c3d223a5c30707 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Thu, 6 Jul 2023 18:01:33 +0200 Subject: [PATCH 04/11] Make service name and fields names required --- script/hassfest/services.py | 51 +++++++++++++++++++-------------- script/hassfest/translations.py | 4 +-- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/script/hassfest/services.py b/script/hassfest/services.py index 96e22768c4bf07..e0e771ee11d606 100644 --- a/script/hassfest/services.py +++ b/script/hassfest/services.py @@ -115,6 +115,15 @@ def validate_services(config: Config, integration: Integration) -> None: # For each service in the integration, check if the description if set, # if not, check if it's in the strings file. If not, add an error. for service_name, service_schema in services.items(): + if "name" not in service_schema: + try: + strings["services"][service_name]["name"] + except KeyError: + integration.add_error( + "services", + f"Service {service_name} has no name and is not in the translations file", + ) + if "description" not in service_schema: try: strings["services"][service_name]["description"] @@ -124,33 +133,33 @@ def validate_services(config: Config, integration: Integration) -> None: f"Service {service_name} has no description and is not in the translations file", ) - # 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 "description" not in field_schema: + # 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 "description" not in field_schema: + try: + strings["services"][service_name]["fields"][field_name][ + "description" + ] + except KeyError: + integration.add_error( + "services", + f"Service {service_name} has a field {field_name} with no description and is not in the translations file", + ) + + if "selector" in field_schema: + with contextlib.suppress(KeyError): + translation_key = field_schema["selector"]["select"][ + "translation_key" + ] try: - strings["services"][service_name]["fields"][field_name][ - "description" - ] + strings["selector"][translation_key] except KeyError: integration.add_error( "services", - f"Service {service_name} has a field {field_name} with no description and is not in the translations file", + 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", ) - if "selector" in field_schema: - with contextlib.suppress(KeyError): - translation_key = field_schema["selector"]["select"][ - "translation_key" - ] - try: - strings["selector"][translation_key] - except KeyError: - integration.add_error( - "services", - 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", - ) - def validate(integrations: dict[str, Integration], config: Config) -> None: """Handle dependencies for integrations.""" diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 954455abc1ac24..0164d1f190f1cd 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -327,11 +327,11 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: ), vol.Optional("services"): cv.schema_with_slug_keys( { - vol.Optional("name"): translation_value_validator, + vol.Required("name"): translation_value_validator, vol.Required("description"): translation_value_validator, vol.Optional("fields"): cv.schema_with_slug_keys( { - vol.Optional("name"): str, + vol.Required("name"): str, vol.Required("description"): translation_value_validator, }, slug_validator=translation_key_validator, From a5702abae9b144eb04c53906c918311cc3ba2318 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 7 Jul 2023 18:35:41 +0200 Subject: [PATCH 05/11] Add services as category to load enabled components on --- homeassistant/helpers/translation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/translation.py b/homeassistant/helpers/translation.py index 96ce9b618c2d9a..79ac3a0c5b73b2 100644 --- a/homeassistant/helpers/translation.py +++ b/homeassistant/helpers/translation.py @@ -302,7 +302,7 @@ async def async_get_translations( components = set(integrations) elif config_flow: components = (await async_get_config_flows(hass)) - hass.config.components - elif category in ("state", "entity_component"): + elif category in ("state", "entity_component", "services"): components = set(hass.config.components) else: # Only 'state' supports merging, so remove platforms from selection From 11361076eb65228c743e07b7796192f3945ce90c Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 10 Jul 2023 21:31:03 +0200 Subject: [PATCH 06/11] Add name & descriptions from translations if available for backwards compat --- homeassistant/helpers/service.py | 32 ++++++++++++++++++++-- tests/helpers/test_service.py | 47 ++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 40bb96506309e8..159f82c7d8f51f 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -50,6 +50,7 @@ device_registry, entity_registry, template, + translation, ) from .selector import TargetSelector from .typing import ConfigType, TemplateVarsType @@ -607,6 +608,11 @@ async def async_get_all_descriptions( ) loaded = dict(zip(missing, contents)) + # Load translations for all service domains + translations = await translation.async_get_translations( + hass, "en", "services", list(services) + ) + # Build response descriptions: dict[str, dict[str, Any]] = {} for domain, services_map in services.items(): @@ -630,12 +636,34 @@ async def async_get_all_descriptions( # Don't warn for missing services, because it triggers false # positives for things like scripts, that register as a service + # + # When name & description are in the translations use those; + # otherwise fallback to backwards compatible behavior from + # the time when we didn't have translations for descriptions yet. + # This mimics the behavior of the frontend. description = { - "name": yaml_description.get("name", ""), - "description": yaml_description.get("description", ""), + "name": translations.get( + f"components.{domain}.services.{service_name}.name", + yaml_description.get("name", ""), + ), + "description": translations.get( + f"components.{domain}.services.{service_name}.description", + yaml_description.get("description", ""), + ), "fields": yaml_description.get("fields", {}), } + # Translate fields names & descriptions as well + for field_name, field_schema in description.get("fields", {}).items(): + if name := translations.get( + f"components.{domain}.services.{service_name}.fields.{field_name}.name" + ): + field_schema["name"] = name + if desc := translations.get( + f"components.{domain}.services.{service_name}.fields.{field_name}.description" + ): + field_schema["description"] = desc + if "target" in yaml_description: description["target"] = yaml_description["target"] diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index bc7a93f0f19d75..999703be93628e 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -1,6 +1,8 @@ """Test service helpers.""" from collections import OrderedDict +from collections.abc import Iterable from copy import deepcopy +from typing import Any from unittest.mock import AsyncMock, Mock, patch import pytest @@ -556,13 +558,49 @@ async def test_async_get_all_descriptions(hass: HomeAssistant) -> None: logger = hass.components.logger logger_config = {logger.DOMAIN: {}} - await async_setup_component(hass, logger.DOMAIN, logger_config) - descriptions = await service.async_get_all_descriptions(hass) + + async def async_get_translations( + hass: HomeAssistant, + language: str, + category: str, + integrations: Iterable[str] | None = None, + config_flow: bool | None = None, + ) -> dict[str, Any]: + """Return all backend translations.""" + translation_key_prefix = ( + f"components.{logger.DOMAIN}.services.set_default_level" + ) + return { + f"{translation_key_prefix}.name": "Translated name", + f"{translation_key_prefix}.description": "Translated description", + f"{translation_key_prefix}.fields.level.name": "Field name", + f"{translation_key_prefix}.fields.level.description": "Field description", + } + + with patch( + "homeassistant.helpers.service.translation.async_get_translations", + side_effect=async_get_translations, + ): + await async_setup_component(hass, logger.DOMAIN, logger_config) + descriptions = await service.async_get_all_descriptions(hass) assert len(descriptions) == 2 - assert "description" in descriptions[logger.DOMAIN]["set_level"] - assert "fields" in descriptions[logger.DOMAIN]["set_level"] + assert descriptions[logger.DOMAIN]["set_default_level"]["name"] == "Translated name" + assert ( + descriptions[logger.DOMAIN]["set_default_level"]["description"] + == "Translated description" + ) + assert ( + descriptions[logger.DOMAIN]["set_default_level"]["fields"]["level"]["name"] + == "Field name" + ) + assert ( + descriptions[logger.DOMAIN]["set_default_level"]["fields"]["level"][ + "description" + ] + == "Field description" + ) hass.services.async_register(logger.DOMAIN, "new_service", lambda x: None, None) service.async_set_service_schema( @@ -602,7 +640,6 @@ async def test_async_get_all_descriptions(hass: HomeAssistant) -> None: "another_service_with_response", {"description": "response service"}, ) - descriptions = await service.async_get_all_descriptions(hass) assert "another_new_service" in descriptions[logger.DOMAIN] assert "service_with_optional_response" in descriptions[logger.DOMAIN] From 19b0c00009c91779703b50fbe4f25a6914b1c05d Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 10 Jul 2023 21:55:13 +0200 Subject: [PATCH 07/11] Make backwards compat actually work --- homeassistant/helpers/service.py | 8 ++++---- tests/helpers/test_service.py | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 159f82c7d8f51f..30524527e0bcb7 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -643,11 +643,11 @@ async def async_get_all_descriptions( # This mimics the behavior of the frontend. description = { "name": translations.get( - f"components.{domain}.services.{service_name}.name", + f"component.{domain}.services.{service_name}.name", yaml_description.get("name", ""), ), "description": translations.get( - f"components.{domain}.services.{service_name}.description", + f"component.{domain}.services.{service_name}.description", yaml_description.get("description", ""), ), "fields": yaml_description.get("fields", {}), @@ -656,11 +656,11 @@ async def async_get_all_descriptions( # Translate fields names & descriptions as well for field_name, field_schema in description.get("fields", {}).items(): if name := translations.get( - f"components.{domain}.services.{service_name}.fields.{field_name}.name" + f"component.{domain}.services.{service_name}.fields.{field_name}.name" ): field_schema["name"] = name if desc := translations.get( - f"components.{domain}.services.{service_name}.fields.{field_name}.description" + f"component.{domain}.services.{service_name}.fields.{field_name}.description" ): field_schema["description"] = desc diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index 999703be93628e..a99f303f6c99f7 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -567,9 +567,7 @@ async def async_get_translations( config_flow: bool | None = None, ) -> dict[str, Any]: """Return all backend translations.""" - translation_key_prefix = ( - f"components.{logger.DOMAIN}.services.set_default_level" - ) + translation_key_prefix = f"component.{logger.DOMAIN}.services.set_default_level" return { f"{translation_key_prefix}.name": "Translated name", f"{translation_key_prefix}.description": "Translated description", From 5976c2eb682f47b45e393b1f0c37368347d30e96 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 11 Jul 2023 08:22:32 +0200 Subject: [PATCH 08/11] Remove kelvin from title --- homeassistant/components/light/strings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index 7043c9e81a800d..b684da95877413 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -279,7 +279,7 @@ "description": "Color temperature for the light in mireds." }, "kelvin": { - "name": "Color temperature (Kelvin)", + "name": "Color temperature", "description": "Color temperature for the light in Kelvin." }, "brightness": { From 1f74c14d086e04c579d1084c33c731901b9b7c28 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 11 Jul 2023 11:49:06 +0200 Subject: [PATCH 09/11] Apply suggestions from code review --- homeassistant/components/light/strings.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index b684da95877413..99f74092c1bb8a 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -252,15 +252,15 @@ }, "rgb_color": { "name": "Color", - "description": "The color for the light (based on RGB - red, green, blue)." + "description": "The color (based on RGB - red, green, blue)." }, "rgbw_color": { "name": "RGBW-color", - "description": "A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color for the light." + "description": "A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color." }, "rgbww_color": { "name": "RGBWW-color", - "description": "A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color for the light." + "description": "A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color." }, "color_name": { "name": "Color name", @@ -268,27 +268,27 @@ }, "hs_color": { "name": "Hue/Sat color", - "description": "Color for the light in hue/sat format. Hue is 0-360 and Sat is 0-100." + "description": "Color in hue/sat format. Hue is 0-360 and Sat is 0-100." }, "xy_color": { "name": "XY-color", - "description": "Color for the light in XY-format." + "description": "Color in XY-format." }, "color_temp": { "name": "Color temperature", - "description": "Color temperature for the light in mireds." + "description": "Color temperature in mireds." }, "kelvin": { "name": "Color temperature", - "description": "Color temperature for the light in Kelvin." + "description": "Color temperature in Kelvin." }, "brightness": { "name": "Brightness value", - "description": "Number indicating brightness, where 0 turns the light off, 1 is the minimum brightness and 255 is the maximum brightness supported by the light." + "description": "Number indicating brightness, where 0 turns the light off, 1 is the minimum brightness, and 255 is the maximum brightness." }, "brightness_pct": { "name": "Brightness", - "description": "Number indicating percentage of full brightness, where 0 turns the light off, 1 is the minimum brightness and 100 is the maximum brightness supported by the light." + "description": "Number indicating the percentage of full brightness, where 0 turns the light off, 1 is the minimum brightness, and 100 is the maximum brightness." }, "brightness_step": { "name": "Brightness step value", From 1e8ee78fc0d6cc48e9a0af3c527ba29c8407565f Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 11 Jul 2023 11:56:36 +0200 Subject: [PATCH 10/11] Process review comments --- homeassistant/components/light/strings.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/light/strings.json b/homeassistant/components/light/strings.json index 99f74092c1bb8a..a4a46d2ca94140 100644 --- a/homeassistant/components/light/strings.json +++ b/homeassistant/components/light/strings.json @@ -252,15 +252,15 @@ }, "rgb_color": { "name": "Color", - "description": "The color (based on RGB - red, green, blue)." + "description": "The color in RGB format. A list of three integers between 0 and 255 representing the values of red, green, and blue." }, "rgbw_color": { "name": "RGBW-color", - "description": "A list containing four integers between 0 and 255 representing the RGBW (red, green, blue, white) color." + "description": "The color in RGBW format. A list of four integers between 0 and 255 representing the values of red, green, blue, and white." }, "rgbww_color": { "name": "RGBWW-color", - "description": "A list containing five integers between 0 and 255 representing the RGBWW (red, green, blue, cold white, warm white) color." + "description": "The color in RGBWW format. A list of five integers between 0 and 255 representing the values of red, green, blue, cold white, and warm white." }, "color_name": { "name": "Color name", @@ -268,11 +268,11 @@ }, "hs_color": { "name": "Hue/Sat color", - "description": "Color in hue/sat format. Hue is 0-360 and Sat is 0-100." + "description": "Color in hue/sat format. A list of two integers. Hue is 0-360 and Sat is 0-100." }, "xy_color": { "name": "XY-color", - "description": "Color in XY-format." + "description": "Color in XY-format. A list of two decimal numbers between 0 and 1." }, "color_temp": { "name": "Color temperature", From 33b02e51fd961297ff4460eaa00b19233f5b21b1 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 11 Jul 2023 15:02:50 +0200 Subject: [PATCH 11/11] Process review comments --- homeassistant/helpers/service.py | 101 ++++++++++++++++--------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index 30524527e0bcb7..1a418a68fd1c9d 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -622,59 +622,62 @@ async def async_get_all_descriptions( for service_name in services_map: cache_key = (domain, service_name) description = descriptions_cache.get(cache_key) + if description is not None: + domain_descriptions[service_name] = description + continue + # Cache missing descriptions - if description is None: - domain_yaml = loaded.get(domain) or {} - # The YAML may be empty for dynamically defined - # services (ie shell_command) that never call - # service.async_set_service_schema for the dynamic - # service - - yaml_description = domain_yaml.get( # type: ignore[union-attr] - service_name, {} - ) + domain_yaml = loaded.get(domain) or {} + # The YAML may be empty for dynamically defined + # services (ie shell_command) that never call + # service.async_set_service_schema for the dynamic + # service + + yaml_description = domain_yaml.get( # type: ignore[union-attr] + service_name, {} + ) - # Don't warn for missing services, because it triggers false - # positives for things like scripts, that register as a service - # - # When name & description are in the translations use those; - # otherwise fallback to backwards compatible behavior from - # the time when we didn't have translations for descriptions yet. - # This mimics the behavior of the frontend. - description = { - "name": translations.get( - f"component.{domain}.services.{service_name}.name", - yaml_description.get("name", ""), - ), - "description": translations.get( - f"component.{domain}.services.{service_name}.description", - yaml_description.get("description", ""), - ), - "fields": yaml_description.get("fields", {}), + # Don't warn for missing services, because it triggers false + # positives for things like scripts, that register as a service + # + # When name & description are in the translations use those; + # otherwise fallback to backwards compatible behavior from + # the time when we didn't have translations for descriptions yet. + # This mimics the behavior of the frontend. + description = { + "name": translations.get( + f"component.{domain}.services.{service_name}.name", + yaml_description.get("name", ""), + ), + "description": translations.get( + f"component.{domain}.services.{service_name}.description", + yaml_description.get("description", ""), + ), + "fields": dict(yaml_description.get("fields", {})), + } + + # Translate fields names & descriptions as well + for field_name, field_schema in description["fields"].items(): + if name := translations.get( + f"component.{domain}.services.{service_name}.fields.{field_name}.name" + ): + field_schema["name"] = name + if desc := translations.get( + f"component.{domain}.services.{service_name}.fields.{field_name}.description" + ): + field_schema["description"] = desc + + if "target" in yaml_description: + description["target"] = yaml_description["target"] + + if ( + response := hass.services.supports_response(domain, service_name) + ) != SupportsResponse.NONE: + description["response"] = { + "optional": response == SupportsResponse.OPTIONAL, } - # Translate fields names & descriptions as well - for field_name, field_schema in description.get("fields", {}).items(): - if name := translations.get( - f"component.{domain}.services.{service_name}.fields.{field_name}.name" - ): - field_schema["name"] = name - if desc := translations.get( - f"component.{domain}.services.{service_name}.fields.{field_name}.description" - ): - field_schema["description"] = desc - - if "target" in yaml_description: - description["target"] = yaml_description["target"] - - if ( - response := hass.services.supports_response(domain, service_name) - ) != SupportsResponse.NONE: - description["response"] = { - "optional": response == SupportsResponse.OPTIONAL, - } - - descriptions_cache[cache_key] = description + descriptions_cache[cache_key] = description domain_descriptions[service_name] = description