1 """Support for the cloud for text-to-speech service."""
3 from __future__
import annotations
8 from hass_nabucasa
import Cloud
9 from hass_nabucasa.voice
import MAP_VOICE, TTS_VOICES, AudioOutput, Gender, VoiceError
10 import voluptuous
as vol
16 PLATFORM_SCHEMA
as TTS_PLATFORM_SCHEMA,
31 from .assist_pipeline
import async_migrate_cloud_pipeline_engine
32 from .client
import CloudClient
33 from .const
import DATA_CLOUD, DATA_PLATFORMS_SETUP, DOMAIN, TTS_ENTITY_UNIQUE_ID
34 from .prefs
import CloudPreferences
36 ATTR_GENDER =
"gender"
38 DEPRECATED_VOICES = {
"XiaoxuanNeural":
"XiaozhenNeural"}
39 SUPPORT_LANGUAGES =
list(TTS_VOICES)
41 _LOGGER = logging.getLogger(__name__)
45 """Validate if platform is deprecated."""
48 "The cloud tts platform configuration is deprecated, "
49 "please remove it from your configuration "
50 "and use the UI to change settings instead"
56 "deprecated_tts_platform_config",
57 breaks_in_ha_version=
"2024.9.0",
59 severity=IssueSeverity.WARNING,
60 translation_key=
"deprecated_tts_platform_config",
66 """Validate chosen gender or language."""
67 if (lang := value.get(CONF_LANG))
is None:
70 if (gender := value.get(ATTR_GENDER))
is None:
71 gender = value[ATTR_GENDER] = next(
72 (chk_gender
for chk_lang, chk_gender
in MAP_VOICE
if chk_lang == lang),
None
75 if (lang, gender)
not in MAP_VOICE:
76 raise vol.Invalid(
"Unsupported language and gender specified.")
81 PLATFORM_SCHEMA = vol.All(
82 TTS_PLATFORM_SCHEMA.extend(
84 vol.Required(CONF_PLATFORM): vol.All(cv.string, _deprecated_platform),
85 vol.Optional(CONF_LANG): str,
86 vol.Optional(ATTR_GENDER): str,
96 discovery_info: DiscoveryInfoType |
None =
None,
98 """Set up Cloud speech component."""
99 cloud = hass.data[DATA_CLOUD]
101 if discovery_info
is not None:
102 discovery_info[
"platform_loaded"].set()
103 return cloud_provider
108 config_entry: ConfigEntry,
109 async_add_entities: AddEntitiesCallback,
111 """Set up Home Assistant Cloud text-to-speech platform."""
112 tts_platform_loaded = hass.data[DATA_PLATFORMS_SETUP][Platform.TTS]
113 tts_platform_loaded.set()
114 cloud = hass.data[DATA_CLOUD]
119 """Home Assistant Cloud text-to-speech entity."""
121 _attr_name =
"Home Assistant Cloud"
122 _attr_unique_id = TTS_ENTITY_UNIQUE_ID
124 def __init__(self, cloud: Cloud[CloudClient]) ->
None:
125 """Initialize cloud text-to-speech entity."""
127 self._language, self.
_voice_voice = cloud.client.prefs.tts_default_voice
130 """Sync preferences."""
131 self._language, self.
_voice_voice = prefs.tts_default_voice
135 """Return the default language."""
136 return self._language
140 """Return a dict include default options."""
142 ATTR_AUDIO_OUTPUT: AudioOutput.MP3,
147 """Return list of supported languages."""
148 return SUPPORT_LANGUAGES
152 """Return list of supported options like voice, emotion."""
154 return [ATTR_GENDER, ATTR_VOICE, ATTR_AUDIO_OUTPUT]
157 """Handle entity which will be added."""
160 async
def pipeline_setup(hass: HomeAssistant, _comp: str) ->
None:
161 """When assist_pipeline is set up."""
162 assert self.
platformplatform.config_entry
163 self.
platformplatform.config_entry.async_create_task(
166 self.
hasshass, platform=Platform.TTS, engine_id=self.
entity_identity_id
173 self.
cloudcloud.client.prefs.async_listen_updates(self.
_sync_prefs_sync_prefs)
178 """Return a list of supported voices for a language."""
179 if not (voices := TTS_VOICES.get(language)):
181 return [Voice(voice, voice)
for voice
in voices]
184 self, message: str, language: str, options: dict[str, Any]
186 """Load TTS from Home Assistant Cloud."""
187 gender: Gender | str |
None = options.get(ATTR_GENDER)
189 original_voice: str |
None = options.get(ATTR_VOICE)
190 if original_voice
is None and language == self._language:
191 original_voice = self.
_voice_voice
193 if voice
not in TTS_VOICES[language]:
194 default_voice = TTS_VOICES[language][0]
196 "Unsupported voice %s detected, falling back to default %s for %s",
201 voice = default_voice
204 data = await self.
cloudcloud.voice.process_tts(
209 output=options[ATTR_AUDIO_OUTPUT],
211 except VoiceError
as err:
212 _LOGGER.error(
"Voice error: %s", err)
215 return (
str(options[ATTR_AUDIO_OUTPUT].value), data)
219 """Home Assistant Cloud speech API provider."""
221 def __init__(self, cloud: Cloud[CloudClient]) ->
None:
222 """Initialize cloud provider."""
224 self.
namename =
"Home Assistant Cloud"
225 self._language, self.
_voice_voice = cloud.client.prefs.tts_default_voice
226 cloud.client.prefs.async_listen_updates(self.
_sync_prefs_sync_prefs)
229 """Sync preferences."""
230 self._language, self.
_voice_voice = prefs.tts_default_voice
234 """Return the default language."""
235 return self._language
239 """Return list of supported languages."""
240 return SUPPORT_LANGUAGES
244 """Return list of supported options like voice, emotion."""
246 return [ATTR_GENDER, ATTR_VOICE, ATTR_AUDIO_OUTPUT]
250 """Return a list of supported voices for a language."""
251 if not (voices := TTS_VOICES.get(language)):
253 return [Voice(voice, voice)
for voice
in voices]
257 """Return a dict include default options."""
259 ATTR_AUDIO_OUTPUT: AudioOutput.MP3,
263 self, message: str, language: str, options: dict[str, Any]
265 """Load TTS from Home Assistant Cloud."""
266 assert self.hass
is not None
267 gender: Gender | str |
None = options.get(ATTR_GENDER)
269 original_voice: str |
None = options.get(ATTR_VOICE)
270 if original_voice
is None and language == self._language:
271 original_voice = self.
_voice_voice
273 if voice
not in TTS_VOICES[language]:
274 default_voice = TTS_VOICES[language][0]
276 "Unsupported voice %s detected, falling back to default %s for %s",
281 voice = default_voice
284 data = await self.
cloudcloud.voice.process_tts(
289 output=options[ATTR_AUDIO_OUTPUT],
291 except VoiceError
as err:
292 _LOGGER.error(
"Voice error: %s", err)
295 return (
str(options[ATTR_AUDIO_OUTPUT].value), data)
301 gender: Gender | str |
None,
303 """Handle deprecated gender."""
312 severity=IssueSeverity.WARNING,
313 breaks_in_ha_version=
"2024.10.0",
314 translation_key=
"deprecated_gender",
315 translation_placeholders={
316 "integration_name":
"Home Assistant Cloud",
317 "deprecated_option":
"gender",
318 "replacement_option":
"voice",
321 return Gender(gender)
327 original_voice: str |
None,
329 """Handle deprecated voice."""
330 voice = original_voice
334 and (voice := DEPRECATED_VOICES.get(original_voice, original_voice))
340 f
"deprecated_voice_{original_voice}",
343 severity=IssueSeverity.WARNING,
344 breaks_in_ha_version=
"2024.8.0",
345 translation_key=
"deprecated_voice",
346 translation_placeholders={
347 "deprecated_voice": original_voice,
348 "replacement_voice": voice,
str|None default_language(self)
None _sync_prefs(self, CloudPreferences prefs)
None __init__(self, Cloud[CloudClient] cloud)
list[Voice]|None async_get_supported_voices(self, str language)
TtsAudioType async_get_tts_audio(self, str message, str language, dict[str, Any] options)
dict[str, Any] default_options(self)
list[str] supported_options(self)
list[str] supported_languages(self)
dict[str, Any] default_options(self)
None _sync_prefs(self, CloudPreferences prefs)
TtsAudioType async_get_tts_audio(self, str message, str language, dict[str, Any] options)
list[str] supported_options(self)
None async_added_to_hass(self)
str default_language(self)
list[Voice]|None async_get_supported_voices(self, str language)
None __init__(self, Cloud[CloudClient] cloud)
list[str] supported_languages(self)
None async_on_remove(self, CALLBACK_TYPE func)
None async_migrate_cloud_pipeline_engine(HomeAssistant hass, Platform platform, str engine_id)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
CloudProvider async_get_engine(HomeAssistant hass, ConfigType config, DiscoveryInfoType|None discovery_info=None)
str|None handle_deprecated_voice(HomeAssistant hass, str|None original_voice)
str _deprecated_platform(str value)
dict[str, Any] validate_lang(dict[str, Any] value)
Gender|None handle_deprecated_gender(HomeAssistant hass, Gender|str|None gender)
None async_create_issue(HomeAssistant hass, str entry_id)
HomeAssistant async_get_hass()
None async_when_setup(core.HomeAssistant hass, str component, Callable[[core.HomeAssistant, str], Awaitable[None]] when_setup_cb)