Add description to tts and notify services (#46764)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
Bram Kragten 2021-02-23 14:29:57 +01:00 committed by GitHub
parent ea4bbd771f
commit afa91e886b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 12 deletions

View File

@ -118,7 +118,7 @@ play_media:
media_content_type: media_content_type:
name: Content type name: Content type
description: 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. video, episode, channel or playlist.
required: true required: true
example: "music" example: "music"

View File

@ -2,7 +2,7 @@
import asyncio import asyncio
from functools import partial from functools import partial
import logging import logging
from typing import Any, Dict, Optional from typing import Any, Dict, Optional, cast
import voluptuous as vol import voluptuous as vol
@ -12,10 +12,12 @@ from homeassistant.core import ServiceCall
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform, discovery from homeassistant.helpers import config_per_platform, discovery
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.helpers.typing import HomeAssistantType 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.setup import async_prepare_setup_platform
from homeassistant.util import slugify from homeassistant.util import slugify
from homeassistant.util.yaml import load_yaml
# mypy: allow-untyped-defs, no-check-untyped-defs # mypy: allow-untyped-defs, no-check-untyped-defs
@ -41,6 +43,9 @@ SERVICE_PERSISTENT_NOTIFICATION = "persistent_notification"
NOTIFY_SERVICES = "notify_services" NOTIFY_SERVICES = "notify_services"
CONF_DESCRIPTION = "description"
CONF_FIELDS = "fields"
PLATFORM_SCHEMA = vol.Schema( PLATFORM_SCHEMA = vol.Schema(
{vol.Required(CONF_PLATFORM): cv.string, vol.Optional(CONF_NAME): cv.string}, {vol.Required(CONF_PLATFORM): cv.string, vol.Optional(CONF_NAME): cv.string},
extra=vol.ALLOW_EXTRA, extra=vol.ALLOW_EXTRA,
@ -161,6 +166,13 @@ class BaseNotificationService:
self._target_service_name_prefix = target_service_name_prefix self._target_service_name_prefix = target_service_name_prefix
self.registered_targets = {} 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: async def async_register_services(self) -> None:
"""Create or update the notify services.""" """Create or update the notify services."""
assert self.hass assert self.hass
@ -185,6 +197,13 @@ class BaseNotificationService:
self._async_notify_message_service, self._async_notify_message_service,
schema=NOTIFY_SERVICE_SCHEMA, 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: for stale_target_name in stale_targets:
del self.registered_targets[stale_target_name] del self.registered_targets[stale_target_name]
@ -203,6 +222,14 @@ class BaseNotificationService:
schema=NOTIFY_SERVICE_SCHEMA, 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: async def async_unregister_services(self) -> None:
"""Unregister the notify services.""" """Unregister the notify services."""
assert self.hass assert self.hass

View File

@ -1,22 +1,37 @@
# Describes the format for available notification services # Describes the format for available notification services
notify: notify:
description: Send a notification. name: Send a notification
description: Sends a notification message to selected notify platforms.
fields: fields:
message: message:
name: Message
description: Message body of the notification. description: Message body of the notification.
example: The garage door has been open for 10 minutes. example: The garage door has been open for 10 minutes.
selector:
text:
title: title:
name: Title
description: Optional title for your notification. description: Optional title for your notification.
example: "Your Garage Door Friend" example: "Your Garage Door Friend"
selector:
text:
target: 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 example: platform specific
data: 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 example: platform specific
selector:
object:
persistent_notification: persistent_notification:
name: Send a persistent notification
description: Sends a notification to the visible in the front-end. description: Sends a notification to the visible in the front-end.
fields: fields:
message: message:
@ -27,10 +42,16 @@ persistent_notification:
example: "Your Garage Door Friend" example: "Your Garage Door Friend"
apns_register: 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: fields:
push_id: 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" example: "72f2a8633655c5ce574fdc9b2b34ff8abdfc3b739b6ceb7a9ff06c1cbbf99f62"
name: name:
description: A friendly name for the device (optional). description: A friendly name for the device (optional).

View File

@ -7,7 +7,7 @@ import logging
import mimetypes import mimetypes
import os import os
import re import re
from typing import Dict, Optional from typing import Dict, Optional, cast
from aiohttp import web from aiohttp import web
import mutagen import mutagen
@ -24,6 +24,7 @@ from homeassistant.components.media_player.const import (
) )
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
CONF_NAME,
CONF_PLATFORM, CONF_PLATFORM,
HTTP_BAD_REQUEST, HTTP_BAD_REQUEST,
HTTP_NOT_FOUND, HTTP_NOT_FOUND,
@ -33,8 +34,11 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_per_platform, discovery from homeassistant.helpers import config_per_platform, discovery
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.network import get_url from homeassistant.helpers.network import get_url
from homeassistant.helpers.service import async_set_service_schema
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.loader import async_get_integration
from homeassistant.setup import async_prepare_setup_platform from homeassistant.setup import async_prepare_setup_platform
from homeassistant.util.yaml import load_yaml
# mypy: allow-untyped-defs, no-check-untyped-defs # mypy: allow-untyped-defs, no-check-untyped-defs
@ -55,6 +59,9 @@ CONF_LANG = "language"
CONF_SERVICE_NAME = "service_name" CONF_SERVICE_NAME = "service_name"
CONF_TIME_MEMORY = "time_memory" CONF_TIME_MEMORY = "time_memory"
CONF_DESCRIPTION = "description"
CONF_FIELDS = "fields"
DEFAULT_CACHE = True DEFAULT_CACHE = True
DEFAULT_CACHE_DIR = "tts" DEFAULT_CACHE_DIR = "tts"
DEFAULT_TIME_MEMORY = 300 DEFAULT_TIME_MEMORY = 300
@ -127,6 +134,13 @@ async def async_setup(hass, config):
hass.http.register_view(TextToSpeechView(tts)) hass.http.register_view(TextToSpeechView(tts))
hass.http.register_view(TextToSpeechUrlView(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): async def async_setup_platform(p_type, p_config=None, discovery_info=None):
"""Set up a TTS platform.""" """Set up a TTS platform."""
if p_config is None: 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 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 = [ setup_tasks = [
asyncio.create_task(async_setup_platform(p_type, p_config)) asyncio.create_task(async_setup_platform(p_type, p_config))
for p_type, p_config in config_per_platform(config, DOMAIN) for p_type, p_config in config_per_platform(config, DOMAIN)

View File

@ -1,23 +1,43 @@
# Describes the format for available TTS services # Describes the format for available TTS services
say: 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: fields:
entity_id: entity_id:
name: Entity
description: Name(s) of media player entities. description: Name(s) of media player entities.
example: "media_player.floor" example: "media_player.floor"
required: true
selector:
entity:
domain: media_player
message: message:
name: Message
description: Text to speak on devices. description: Text to speak on devices.
example: "My name is hanna" example: "My name is hanna"
required: true
selector:
text:
cache: cache:
name: Cache
description: Control file cache of this message. description: Control file cache of this message.
example: "true" example: "true"
default: false
selector:
boolean:
language: language:
name: Language
description: Language to use for speech generation. description: Language to use for speech generation.
example: "ru" example: "ru"
selector:
text:
options: 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 example: platform specific
clear_cache: clear_cache:
description: Remove cache files and RAM cache. name: Clear TTS cache
description: Remove all text-to-speech cache files and RAM cache.

View File

@ -478,6 +478,9 @@ def async_set_service_schema(
"fields": schema.get("fields", {}), "fields": schema.get("fields", {}),
} }
if "target" in schema:
description["target"] = schema["target"]
hass.data[SERVICE_DESCRIPTION_CACHE][f"{domain}.{service}"] = description hass.data[SERVICE_DESCRIPTION_CACHE][f"{domain}.{service}"] = description