1 """Support for Wyoming text-to-speech services."""
3 from collections
import defaultdict
8 from wyoming.audio
import AudioChunk, AudioStop
9 from wyoming.client
import AsyncTcpClient
10 from wyoming.tts
import Synthesize, SynthesizeVoice
17 from .const
import ATTR_SPEAKER, DOMAIN
18 from .data
import WyomingService
19 from .error
import WyomingError
20 from .models
import DomainDataItem
22 _LOGGER = logging.getLogger(__name__)
27 config_entry: ConfigEntry,
28 async_add_entities: AddEntitiesCallback,
30 """Set up Wyoming speech-to-text."""
31 item: DomainDataItem = hass.data[DOMAIN][config_entry.entry_id]
40 """Wyoming text-to-speech provider."""
44 config_entry: ConfigEntry,
45 service: WyomingService,
47 """Set up provider."""
49 self.
_tts_service_tts_service = next(tts
for tts
in service.info.tts
if tts.installed)
51 voice_languages: set[str] = set()
52 self._voices: dict[str, list[
tts.Voice]] = defaultdict(list)
54 if not voice.installed:
57 voice_languages.update(voice.languages)
58 for language
in voice.languages:
59 self._voices[language].append(
62 name=voice.description
or voice.name,
67 for language
in self._voices:
68 self._voices[language] = sorted(
69 self._voices[language], key=
lambda v: v.name
72 self._supported_languages: list[str] =
list(voice_languages)
79 """Return default language."""
80 if not self._supported_languages:
83 return self._supported_languages[0]
87 """Return list of supported languages."""
88 return self._supported_languages
92 """Return list of supported options like voice, emotion."""
94 tts.ATTR_AUDIO_OUTPUT,
101 """Return a dict include default options."""
106 """Return a list of supported voices for a language."""
107 return self._voices.
get(language)
110 """Load TTS from TCP socket."""
111 voice_name: str |
None = options.get(tts.ATTR_VOICE)
112 voice_speaker: str |
None = options.get(ATTR_SPEAKER)
115 async
with AsyncTcpClient(self.
serviceservice.host, self.
serviceservice.port)
as client:
116 voice: SynthesizeVoice |
None =
None
117 if voice_name
is not None:
118 voice = SynthesizeVoice(name=voice_name, speaker=voice_speaker)
120 synthesize = Synthesize(text=message, voice=voice)
121 await client.write_event(synthesize.event())
123 with io.BytesIO()
as wav_io:
124 wav_writer: wave.Wave_write |
None =
None
126 event = await client.read_event()
128 _LOGGER.debug(
"Connection lost")
131 if AudioStop.is_type(event.type):
134 if AudioChunk.is_type(event.type):
135 chunk = AudioChunk.from_event(event)
136 if wav_writer
is None:
137 wav_writer = wave.open(wav_io,
"wb")
138 wav_writer.setframerate(chunk.rate)
139 wav_writer.setsampwidth(chunk.width)
140 wav_writer.setnchannels(chunk.channels)
142 wav_writer.writeframes(chunk.audio)
144 if wav_writer
is not None:
147 data = wav_io.getvalue()
149 except (OSError, WyomingError):
def default_options(self)
def supported_languages(self)
list[tts.Voice]|None async_get_supported_voices(self, str language)
def supported_options(self)
def default_language(self)
None __init__(self, ConfigEntry config_entry, WyomingService service)
def async_get_tts_audio(self, message, language, options)
web.Response get(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)