Home Assistant Unofficial Reference 2024.12.1
tts.py
Go to the documentation of this file.
1 """Support for the ElevenLabs text-to-speech service."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from types import MappingProxyType
7 from typing import Any
8 
9 from elevenlabs.client import AsyncElevenLabs
10 from elevenlabs.core import ApiError
11 from elevenlabs.types import Model, Voice as ElevenLabsVoice, VoiceSettings
12 
13 from homeassistant.components.tts import (
14  ATTR_VOICE,
15  TextToSpeechEntity,
16  TtsAudioType,
17  Voice,
18 )
19 from homeassistant.core import HomeAssistant
20 from homeassistant.exceptions import HomeAssistantError
21 from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 
24 from . import EleventLabsConfigEntry
25 from .const import (
26  CONF_OPTIMIZE_LATENCY,
27  CONF_SIMILARITY,
28  CONF_STABILITY,
29  CONF_STYLE,
30  CONF_USE_SPEAKER_BOOST,
31  CONF_VOICE,
32  DEFAULT_OPTIMIZE_LATENCY,
33  DEFAULT_SIMILARITY,
34  DEFAULT_STABILITY,
35  DEFAULT_STYLE,
36  DEFAULT_USE_SPEAKER_BOOST,
37  DOMAIN,
38 )
39 
40 _LOGGER = logging.getLogger(__name__)
41 
42 
43 def to_voice_settings(options: MappingProxyType[str, Any]) -> VoiceSettings:
44  """Return voice settings."""
45  return VoiceSettings(
46  stability=options.get(CONF_STABILITY, DEFAULT_STABILITY),
47  similarity_boost=options.get(CONF_SIMILARITY, DEFAULT_SIMILARITY),
48  style=options.get(CONF_STYLE, DEFAULT_STYLE),
49  use_speaker_boost=options.get(
50  CONF_USE_SPEAKER_BOOST, DEFAULT_USE_SPEAKER_BOOST
51  ),
52  )
53 
54 
56  hass: HomeAssistant,
57  config_entry: EleventLabsConfigEntry,
58  async_add_entities: AddEntitiesCallback,
59 ) -> None:
60  """Set up ElevenLabs tts platform via config entry."""
61  client = config_entry.runtime_data.client
62  voices = (await client.voices.get_all()).voices
63  default_voice_id = config_entry.options[CONF_VOICE]
64  voice_settings = to_voice_settings(config_entry.options)
66  [
68  client,
69  config_entry.runtime_data.model,
70  voices,
71  default_voice_id,
72  config_entry.entry_id,
73  config_entry.title,
74  voice_settings,
75  config_entry.options.get(
76  CONF_OPTIMIZE_LATENCY, DEFAULT_OPTIMIZE_LATENCY
77  ),
78  )
79  ]
80  )
81 
82 
84  """The ElevenLabs API entity."""
85 
86  _attr_supported_options = [ATTR_VOICE]
87 
88  def __init__(
89  self,
90  client: AsyncElevenLabs,
91  model: Model,
92  voices: list[ElevenLabsVoice],
93  default_voice_id: str,
94  entry_id: str,
95  title: str,
96  voice_settings: VoiceSettings,
97  latency: int = 0,
98  ) -> None:
99  """Init ElevenLabs TTS service."""
100  self._client_client = client
101  self._model_model = model
102  self._default_voice_id_default_voice_id = default_voice_id
103  self._voices_voices = sorted(
104  (Voice(v.voice_id, v.name) for v in voices if v.name),
105  key=lambda v: v.name,
106  )
107  # Default voice first
108  voice_indices = [
109  idx for idx, v in enumerate(self._voices_voices) if v.voice_id == default_voice_id
110  ]
111  if voice_indices:
112  self._voices_voices.insert(0, self._voices_voices.pop(voice_indices[0]))
113  self._voice_settings_voice_settings = voice_settings
114  self._latency_latency = latency
115 
116  # Entity attributes
117  self._attr_unique_id_attr_unique_id = entry_id
118  self._attr_name_attr_name = title
119  self._attr_device_info_attr_device_info = DeviceInfo(
120  identifiers={(DOMAIN, entry_id)},
121  manufacturer="ElevenLabs",
122  model=model.name,
123  entry_type=DeviceEntryType.SERVICE,
124  )
125  self._attr_supported_languages_attr_supported_languages = [
126  lang.language_id for lang in self._model_model.languages or []
127  ]
128  self._attr_default_language_attr_default_language = self.supported_languagessupported_languages[0]
129 
130  def async_get_supported_voices(self, language: str) -> list[Voice]:
131  """Return a list of supported voices for a language."""
132  return self._voices_voices
133 
135  self, message: str, language: str, options: dict[str, Any]
136  ) -> TtsAudioType:
137  """Load tts audio file from the engine."""
138  _LOGGER.debug("Getting TTS audio for %s", message)
139  _LOGGER.debug("Options: %s", options)
140  voice_id = options.get(ATTR_VOICE, self._default_voice_id_default_voice_id)
141  try:
142  audio = await self._client_client.generate(
143  text=message,
144  voice=voice_id,
145  optimize_streaming_latency=self._latency_latency,
146  voice_settings=self._voice_settings_voice_settings,
147  model=self._model_model.model_id,
148  )
149  bytes_combined = b"".join([byte_seg async for byte_seg in audio])
150  except ApiError as exc:
151  _LOGGER.warning(
152  "Error during processing of TTS request %s", exc, exc_info=True
153  )
154  raise HomeAssistantError(exc) from exc
155  return "mp3", bytes_combined
None __init__(self, AsyncElevenLabs client, Model model, list[ElevenLabsVoice] voices, str default_voice_id, str entry_id, str title, VoiceSettings voice_settings, int latency=0)
Definition: tts.py:98
list[Voice] async_get_supported_voices(self, str language)
Definition: tts.py:130
TtsAudioType async_get_tts_audio(self, str message, str language, dict[str, Any] options)
Definition: tts.py:136
None async_setup_entry(HomeAssistant hass, EleventLabsConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: tts.py:59
VoiceSettings to_voice_settings(MappingProxyType[str, Any] options)
Definition: tts.py:43