Home Assistant Unofficial Reference 2024.12.1
helpers.py
Go to the documentation of this file.
1 """Helper classes for Google Cloud integration."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Mapping
6 import functools
7 import operator
8 from typing import Any
9 
10 from google.cloud import texttospeech
11 from google.oauth2.service_account import Credentials
12 import voluptuous as vol
13 
14 from homeassistant.components.tts import CONF_LANG
17  NumberSelector,
18  NumberSelectorConfig,
19  SelectSelector,
20  SelectSelectorConfig,
21  SelectSelectorMode,
22 )
23 
24 from .const import (
25  CONF_ENCODING,
26  CONF_GAIN,
27  CONF_GENDER,
28  CONF_KEY_FILE,
29  CONF_PITCH,
30  CONF_PROFILES,
31  CONF_SPEED,
32  CONF_TEXT_TYPE,
33  CONF_VOICE,
34  DEFAULT_LANG,
35 )
36 
37 DEFAULT_VOICE = ""
38 
39 
40 async def async_tts_voices(
41  client: texttospeech.TextToSpeechAsyncClient,
42 ) -> dict[str, list[str]]:
43  """Get TTS voice model names keyed by language."""
44  voices: dict[str, list[str]] = {}
45  list_voices_response = await client.list_voices()
46  for voice in list_voices_response.voices:
47  language_code = voice.language_codes[0]
48  if language_code not in voices:
49  voices[language_code] = []
50  voices[language_code].append(voice.name)
51  return voices
52 
53 
55  config_options: Mapping[str, Any],
56  voices: dict[str, list[str]],
57  from_config_flow: bool = False,
58 ) -> vol.Schema:
59  """Return schema for TTS options with default values from config or constants."""
60  # If we are called from the config flow we want the defaults to be from constants
61  # to allow clearing the current value (passed as suggested_value) in the UI.
62  # If we aren't called from the config flow we want the defaults to be from the config.
63  defaults = {} if from_config_flow else config_options
64  return vol.Schema(
65  {
66  vol.Optional(
67  CONF_GENDER,
68  default=defaults.get(
69  CONF_GENDER,
70  texttospeech.SsmlVoiceGender.NEUTRAL.name, # type: ignore[attr-defined]
71  ),
72  ): vol.All(
73  vol.Upper,
76  mode=SelectSelectorMode.DROPDOWN,
77  options=list(texttospeech.SsmlVoiceGender.__members__),
78  )
79  ),
80  ),
81  vol.Optional(
82  CONF_VOICE,
83  default=defaults.get(CONF_VOICE, DEFAULT_VOICE),
84  ): SelectSelector(
86  mode=SelectSelectorMode.DROPDOWN,
87  options=["", *functools.reduce(operator.iadd, voices.values(), [])],
88  )
89  ),
90  vol.Optional(
91  CONF_ENCODING,
92  default=defaults.get(
93  CONF_ENCODING,
94  texttospeech.AudioEncoding.MP3.name, # type: ignore[attr-defined]
95  ),
96  ): vol.All(
97  vol.Upper,
100  mode=SelectSelectorMode.DROPDOWN,
101  options=list(texttospeech.AudioEncoding.__members__),
102  )
103  ),
104  ),
105  vol.Optional(
106  CONF_SPEED,
107  default=defaults.get(CONF_SPEED, 1.0),
108  ): NumberSelector(NumberSelectorConfig(min=0.25, max=4.0, step=0.01)),
109  vol.Optional(
110  CONF_PITCH,
111  default=defaults.get(CONF_PITCH, 0),
112  ): NumberSelector(NumberSelectorConfig(min=-20.0, max=20.0, step=0.1)),
113  vol.Optional(
114  CONF_GAIN,
115  default=defaults.get(CONF_GAIN, 0),
116  ): NumberSelector(NumberSelectorConfig(min=-96.0, max=16.0, step=0.1)),
117  vol.Optional(
118  CONF_PROFILES,
119  default=defaults.get(CONF_PROFILES, []),
120  ): SelectSelector(
122  mode=SelectSelectorMode.DROPDOWN,
123  options=[
124  # https://cloud.google.com/text-to-speech/docs/audio-profiles
125  "wearable-class-device",
126  "handset-class-device",
127  "headphone-class-device",
128  "small-bluetooth-speaker-class-device",
129  "medium-bluetooth-speaker-class-device",
130  "large-home-entertainment-class-device",
131  "large-automotive-class-device",
132  "telephony-class-application",
133  ],
134  multiple=True,
135  sort=False,
136  )
137  ),
138  vol.Optional(
139  CONF_TEXT_TYPE,
140  default=defaults.get(CONF_TEXT_TYPE, "text"),
141  ): vol.All(
142  vol.Lower,
145  mode=SelectSelectorMode.DROPDOWN,
146  options=["text", "ssml"],
147  )
148  ),
149  ),
150  }
151  )
152 
153 
154 def tts_platform_schema() -> vol.Schema:
155  """Return schema for TTS platform."""
156  return vol.Schema(
157  {
158  vol.Optional(CONF_KEY_FILE): cv.string,
159  vol.Optional(CONF_LANG, default=DEFAULT_LANG): cv.matches_regex(
160  r"[a-z]{2,3}-[A-Z]{2}|"
161  ),
162  **tts_options_schema({}, {}).schema,
163  vol.Optional(CONF_VOICE, default=DEFAULT_VOICE): cv.matches_regex(
164  r"[a-z]{2,3}-[A-Z]{2}-.*-[A-Z]|"
165  ),
166  }
167  )
168 
169 
170 def validate_service_account_info(info: Mapping[str, str]) -> None:
171  """Validate service account info.
172 
173  Args:
174  info: The service account info in Google format.
175 
176  Raises:
177  ValueError: If the info is not in the expected format.
178 
179  """
180  Credentials.from_service_account_info(info) # type:ignore[no-untyped-call]
dict[str, list[str]] async_tts_voices(texttospeech.TextToSpeechAsyncClient client)
Definition: helpers.py:42
vol.Schema tts_options_schema(Mapping[str, Any] config_options, dict[str, list[str]] voices, bool from_config_flow=False)
Definition: helpers.py:58
None validate_service_account_info(Mapping[str, str] info)
Definition: helpers.py:170