diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 03084f0f1a1..eaca8483be1 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -118,7 +118,7 @@ play_media: media_content_type: name: Content type description: - The type of the content to play. Must be one of image, music, tvshow, + The type of the content to play. Like image, music, tvshow, video, episode, channel or playlist. required: true example: "music" diff --git a/homeassistant/components/notify/__init__.py b/homeassistant/components/notify/__init__.py index 1e9c7d8595a..7be66dc3c59 100644 --- a/homeassistant/components/notify/__init__.py +++ b/homeassistant/components/notify/__init__.py @@ -2,7 +2,7 @@ import asyncio from functools import partial import logging -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, cast import voluptuous as vol @@ -12,10 +12,12 @@ from homeassistant.core import ServiceCall from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, discovery import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.service import async_set_service_schema from homeassistant.helpers.typing import HomeAssistantType -from homeassistant.loader import bind_hass +from homeassistant.loader import async_get_integration, bind_hass from homeassistant.setup import async_prepare_setup_platform from homeassistant.util import slugify +from homeassistant.util.yaml import load_yaml # mypy: allow-untyped-defs, no-check-untyped-defs @@ -41,6 +43,9 @@ SERVICE_PERSISTENT_NOTIFICATION = "persistent_notification" NOTIFY_SERVICES = "notify_services" +CONF_DESCRIPTION = "description" +CONF_FIELDS = "fields" + PLATFORM_SCHEMA = vol.Schema( {vol.Required(CONF_PLATFORM): cv.string, vol.Optional(CONF_NAME): cv.string}, extra=vol.ALLOW_EXTRA, @@ -161,6 +166,13 @@ class BaseNotificationService: self._target_service_name_prefix = target_service_name_prefix self.registered_targets = {} + # Load service descriptions from notify/services.yaml + integration = await async_get_integration(hass, DOMAIN) + services_yaml = integration.file_path / "services.yaml" + self.services_dict = cast( + dict, await hass.async_add_executor_job(load_yaml, str(services_yaml)) + ) + async def async_register_services(self) -> None: """Create or update the notify services.""" assert self.hass @@ -185,6 +197,13 @@ class BaseNotificationService: self._async_notify_message_service, schema=NOTIFY_SERVICE_SCHEMA, ) + # Register the service description + service_desc = { + CONF_NAME: f"Send a notification via {target_name}", + CONF_DESCRIPTION: f"Sends a notification message using the {target_name} integration.", + CONF_FIELDS: self.services_dict[SERVICE_NOTIFY][CONF_FIELDS], + } + async_set_service_schema(self.hass, DOMAIN, target_name, service_desc) for stale_target_name in stale_targets: del self.registered_targets[stale_target_name] @@ -203,6 +222,14 @@ class BaseNotificationService: schema=NOTIFY_SERVICE_SCHEMA, ) + # Register the service description + service_desc = { + CONF_NAME: f"Send a notification with {self._service_name}", + CONF_DESCRIPTION: f"Sends a notification message using the {self._service_name} service.", + CONF_FIELDS: self.services_dict[SERVICE_NOTIFY][CONF_FIELDS], + } + async_set_service_schema(self.hass, DOMAIN, self._service_name, service_desc) + async def async_unregister_services(self) -> None: """Unregister the notify services.""" assert self.hass diff --git a/homeassistant/components/notify/services.yaml b/homeassistant/components/notify/services.yaml index 8c75c94e34a..f6918b6c09c 100644 --- a/homeassistant/components/notify/services.yaml +++ b/homeassistant/components/notify/services.yaml @@ -1,22 +1,37 @@ # Describes the format for available notification services notify: - description: Send a notification. + name: Send a notification + description: Sends a notification message to selected notify platforms. fields: message: + name: Message description: Message body of the notification. example: The garage door has been open for 10 minutes. + selector: + text: title: + name: Title description: Optional title for your notification. example: "Your Garage Door Friend" + selector: + text: target: - description: An array of targets to send the notification to. Optional depending on the platform. + description: + An array of targets to send the notification to. Optional depending on + the platform. example: platform specific data: - description: Extended information for notification. Optional depending on the platform. + name: Data + description: + Extended information for notification. Optional depending on the + platform. example: platform specific + selector: + object: persistent_notification: + name: Send a persistent notification description: Sends a notification to the visible in the front-end. fields: message: @@ -27,10 +42,16 @@ persistent_notification: example: "Your Garage Door Friend" apns_register: - description: Registers a device to receive push notifications. + name: Register APNS device + description: + Registers a device to receive push notifications via APNS (Apple Push + Notification Service). fields: push_id: - description: The device token, a 64 character hex string (256 bits). The device token is provided to you by your client app, which receives the token after registering itself with the remote notification service. + description: + The device token, a 64 character hex string (256 bits). The device token + is provided to you by your client app, which receives the token after + registering itself with the remote notification service. example: "72f2a8633655c5ce574fdc9b2b34ff8abdfc3b739b6ceb7a9ff06c1cbbf99f62" name: description: A friendly name for the device (optional). diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index ff1bf946e83..67bc933a530 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -7,7 +7,7 @@ import logging import mimetypes import os import re -from typing import Dict, Optional +from typing import Dict, Optional, cast from aiohttp import web import mutagen @@ -24,6 +24,7 @@ from homeassistant.components.media_player.const import ( ) from homeassistant.const import ( ATTR_ENTITY_ID, + CONF_NAME, CONF_PLATFORM, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, @@ -33,8 +34,11 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_per_platform, discovery import homeassistant.helpers.config_validation as cv from homeassistant.helpers.network import get_url +from homeassistant.helpers.service import async_set_service_schema from homeassistant.helpers.typing import HomeAssistantType +from homeassistant.loader import async_get_integration from homeassistant.setup import async_prepare_setup_platform +from homeassistant.util.yaml import load_yaml # mypy: allow-untyped-defs, no-check-untyped-defs @@ -55,6 +59,9 @@ CONF_LANG = "language" CONF_SERVICE_NAME = "service_name" CONF_TIME_MEMORY = "time_memory" +CONF_DESCRIPTION = "description" +CONF_FIELDS = "fields" + DEFAULT_CACHE = True DEFAULT_CACHE_DIR = "tts" DEFAULT_TIME_MEMORY = 300 @@ -127,6 +134,13 @@ async def async_setup(hass, config): hass.http.register_view(TextToSpeechView(tts)) hass.http.register_view(TextToSpeechUrlView(tts)) + # Load service descriptions from tts/services.yaml + integration = await async_get_integration(hass, DOMAIN) + services_yaml = integration.file_path / "services.yaml" + services_dict = cast( + dict, await hass.async_add_executor_job(load_yaml, str(services_yaml)) + ) + async def async_setup_platform(p_type, p_config=None, discovery_info=None): """Set up a TTS platform.""" if p_config is None: @@ -193,6 +207,14 @@ async def async_setup(hass, config): DOMAIN, service_name, async_say_handle, schema=SCHEMA_SERVICE_SAY ) + # Register the service description + service_desc = { + CONF_NAME: "Say an TTS message with {p_type}", + CONF_DESCRIPTION: f"Say something using text-to-speech on a media player with {p_type}.", + CONF_FIELDS: services_dict[SERVICE_SAY][CONF_FIELDS], + } + async_set_service_schema(hass, DOMAIN, service_name, service_desc) + setup_tasks = [ asyncio.create_task(async_setup_platform(p_type, p_config)) for p_type, p_config in config_per_platform(config, DOMAIN) diff --git a/homeassistant/components/tts/services.yaml b/homeassistant/components/tts/services.yaml index 7d1bf95572b..2b48dd39dee 100644 --- a/homeassistant/components/tts/services.yaml +++ b/homeassistant/components/tts/services.yaml @@ -1,23 +1,43 @@ # Describes the format for available TTS services say: - description: Say some things on a media player. + name: Say an TTS message + description: Say something using text-to-speech on a media player. fields: entity_id: + name: Entity description: Name(s) of media player entities. example: "media_player.floor" + required: true + selector: + entity: + domain: media_player message: + name: Message description: Text to speak on devices. example: "My name is hanna" + required: true + selector: + text: cache: + name: Cache description: Control file cache of this message. example: "true" + default: false + selector: + boolean: language: + name: Language description: Language to use for speech generation. example: "ru" + selector: + text: options: - description: A dictionary containing platform-specific options. Optional depending on the platform. + description: + A dictionary containing platform-specific options. Optional depending on + the platform. example: platform specific clear_cache: - description: Remove cache files and RAM cache. + name: Clear TTS cache + description: Remove all text-to-speech cache files and RAM cache. diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index a55ba8a84af..7983190dbe8 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -478,6 +478,9 @@ def async_set_service_schema( "fields": schema.get("fields", {}), } + if "target" in schema: + description["target"] = schema["target"] + hass.data[SERVICE_DESCRIPTION_CACHE][f"{domain}.{service}"] = description