diff --git a/homeassistant/components/tts/__init__.py b/homeassistant/components/tts/__init__.py index 5e556e62c85b..71adc8a98752 100644 --- a/homeassistant/components/tts/__init__.py +++ b/homeassistant/components/tts/__init__.py @@ -35,20 +35,19 @@ from homeassistant.core import ( HassJob, HassJobType, HomeAssistant, - ServiceCall, callback, ) from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.event import async_call_later from homeassistant.helpers.network import get_url from homeassistant.helpers.typing import UNDEFINED, ConfigType from homeassistant.util import language as language_util, ulid as ulid_util -from .const import ( +from .const import ( # noqa: F401 ATTR_CACHE, ATTR_LANGUAGE, + ATTR_MEDIA_PLAYER_ENTITY_ID, ATTR_MESSAGE, ATTR_OPTIONS, CONF_CACHE, @@ -61,6 +60,7 @@ from .const import ( DEFAULT_TIME_MEMORY, DOMAIN, MEDIA_SOURCE_STREAM_PATH, + SERVICE_CLEAR_CACHE, TtsAudioType, ) from .entity import TextToSpeechEntity, TTSAudioRequest, TTSAudioResponse @@ -68,6 +68,7 @@ from .helper import get_engine_instance from .legacy import PLATFORM_SCHEMA, PLATFORM_SCHEMA_BASE, Provider, async_setup_legacy from .media_source import generate_media_source_id, parse_media_source_id from .models import Voice +from .services import async_setup_services __all__ = [ "ATTR_AUDIO_OUTPUT", @@ -101,7 +102,6 @@ ATTR_PREFERRED_SAMPLE_RATE = "preferred_sample_rate" ATTR_PREFERRED_SAMPLE_CHANNELS = "preferred_sample_channels" ATTR_PREFERRED_SAMPLE_BYTES = "preferred_sample_bytes" ATTR_PREFERRED_BITRATE = "preferred_bitrate" -ATTR_MEDIA_PLAYER_ENTITY_ID = "media_player_entity_id" ATTR_VOICE = "voice" _DEFAULT_FORMAT = "mp3" @@ -115,8 +115,6 @@ _PREFFERED_FORMAT_OPTIONS: Final[set[str]] = { CONF_LANG = "language" -SERVICE_CLEAR_CACHE = "clear_cache" - _RE_LEGACY_VOICE_FILE = re.compile( r"([a-f0-9]{40})_([^_]+)_([^_]+)_([a-z_]+)\.[a-z0-9]{3,4}" ) @@ -125,8 +123,6 @@ _RE_VOICE_FILE = re.compile( ) KEY_PATTERN = "{0}_{1}_{2}_{3}" -SCHEMA_SERVICE_CLEAR_CACHE = probatio.Schema({}) - FFMPEG_CHUNK_SIZE: Final[int] = 4096 @@ -445,28 +441,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: platform_setups = await async_setup_legacy(hass, config) - component.async_register_entity_service( - "speak", - { - probatio.Required(ATTR_MEDIA_PLAYER_ENTITY_ID): cv.comp_entity_ids, - probatio.Required(ATTR_MESSAGE): cv.string, - probatio.Optional(ATTR_CACHE, default=DEFAULT_CACHE): cv.boolean, - probatio.Optional(ATTR_LANGUAGE): cv.string, - probatio.Optional(ATTR_OPTIONS): dict, - }, - "async_speak", - ) - - async def async_clear_cache_handle(service: ServiceCall) -> None: - """Handle clear cache service call.""" - await tts.async_clear_cache() - - hass.services.async_register( - DOMAIN, - SERVICE_CLEAR_CACHE, - async_clear_cache_handle, - schema=SCHEMA_SERVICE_CLEAR_CACHE, - ) + async_setup_services(hass) for setup in platform_setups: # Tasks are created as tracked tasks to ensure startup diff --git a/homeassistant/components/tts/const.py b/homeassistant/components/tts/const.py index 140d493b2b31..ecede41fcb50 100644 --- a/homeassistant/components/tts/const.py +++ b/homeassistant/components/tts/const.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: ATTR_CACHE = "cache" ATTR_LANGUAGE = "language" +ATTR_MEDIA_PLAYER_ENTITY_ID = "media_player_entity_id" ATTR_MESSAGE = "message" ATTR_OPTIONS = "options" @@ -30,4 +31,6 @@ DATA_TTS_MANAGER: HassKey[SpeechManager] = HassKey("tts_manager") MEDIA_SOURCE_STREAM_PATH = "-stream-" +SERVICE_CLEAR_CACHE = "clear_cache" + type TtsAudioType = tuple[str | None, bytes | None] diff --git a/homeassistant/components/tts/notify.py b/homeassistant/components/tts/notify.py index de17ab9583a4..5457e1c26271 100644 --- a/homeassistant/components/tts/notify.py +++ b/homeassistant/components/tts/notify.py @@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant, split_entity_id from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import ATTR_LANGUAGE, ATTR_MEDIA_PLAYER_ENTITY_ID, ATTR_MESSAGE, DOMAIN +from .const import ATTR_LANGUAGE, ATTR_MEDIA_PLAYER_ENTITY_ID, ATTR_MESSAGE, DOMAIN CONF_MEDIA_PLAYER = "media_player" CONF_TTS_SERVICE = "tts_service" diff --git a/homeassistant/components/tts/services.py b/homeassistant/components/tts/services.py new file mode 100644 index 000000000000..9a824f1db631 --- /dev/null +++ b/homeassistant/components/tts/services.py @@ -0,0 +1,49 @@ +"""Services for the Text-to-speech integration.""" + +import probatio + +from homeassistant.core import HomeAssistant, ServiceCall, callback +from homeassistant.helpers import config_validation as cv + +from .const import ( + ATTR_CACHE, + ATTR_LANGUAGE, + ATTR_MEDIA_PLAYER_ENTITY_ID, + ATTR_MESSAGE, + ATTR_OPTIONS, + DATA_COMPONENT, + DATA_TTS_MANAGER, + DEFAULT_CACHE, + DOMAIN, + SERVICE_CLEAR_CACHE, +) + +SCHEMA_SERVICE_CLEAR_CACHE = probatio.Schema({}) + + +async def _async_clear_cache_handle(service: ServiceCall) -> None: + """Handle clear cache service call.""" + await service.hass.data[DATA_TTS_MANAGER].async_clear_cache() + + +@callback +def async_setup_services(hass: HomeAssistant) -> None: + """Register the Text-to-speech services.""" + hass.data[DATA_COMPONENT].async_register_entity_service( + "speak", + { + probatio.Required(ATTR_MEDIA_PLAYER_ENTITY_ID): cv.comp_entity_ids, + probatio.Required(ATTR_MESSAGE): cv.string, + probatio.Optional(ATTR_CACHE, default=DEFAULT_CACHE): cv.boolean, + probatio.Optional(ATTR_LANGUAGE): cv.string, + probatio.Optional(ATTR_OPTIONS): dict, + }, + "async_speak", + ) + + hass.services.async_register( + DOMAIN, + SERVICE_CLEAR_CACHE, + _async_clear_cache_handle, + schema=SCHEMA_SERVICE_CLEAR_CACHE, + )