mirror of
https://github.com/home-assistant/core.git
synced 2025-08-06 22:25:13 +02:00
* Added profiles parameter
* Changed supported languages and encodings values * Added parameters validations
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"domain": "google_cloud",
|
"domain": "google_cloud",
|
||||||
"name": "Google Cloud TTS",
|
"name": "Google Cloud Platform",
|
||||||
"documentation": "https://www.home-assistant.io/components/google_cloud",
|
"documentation": "https://www.home-assistant.io/components/google_cloud",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"google-cloud-texttospeech==0.4.0"
|
"google-cloud-texttospeech==0.4.0"
|
||||||
|
@@ -17,32 +17,20 @@ CONF_ENCODING = 'encoding'
|
|||||||
CONF_SPEED = 'speed'
|
CONF_SPEED = 'speed'
|
||||||
CONF_PITCH = 'pitch'
|
CONF_PITCH = 'pitch'
|
||||||
CONF_GAIN = 'gain'
|
CONF_GAIN = 'gain'
|
||||||
|
CONF_PROFILES = 'profiles'
|
||||||
|
|
||||||
SUPPORTED_LANGUAGES = [
|
SUPPORTED_LANGUAGES = [
|
||||||
'en', 'da', 'nl', 'fr', 'de', 'it', 'ja', 'ko', 'nb',
|
'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-US', 'es-ES', 'fr-CA', 'fr-FR',
|
||||||
'pl', 'pt', 'ru', 'sk', 'es', 'sv', 'tr', 'uk',
|
'it-IT', 'ja-JP', 'ko-KR', 'nb-NO', 'nl-NL', 'pl-PL', 'pt-BR', 'pt-PT',
|
||||||
|
'ru-RU', 'sk-SK', 'sv-SE', 'tr-TR', 'uk-UA',
|
||||||
]
|
]
|
||||||
DEFAULT_LANG = SUPPORTED_LANGUAGES[0]
|
DEFAULT_LANG = 'en-US'
|
||||||
|
|
||||||
SUPPORTED_GENDERS = [
|
SUPPORTED_GENDERS = ['NEUTRAL', 'FEMALE', 'MALE']
|
||||||
'Neutral', 'Female', 'Male',
|
DEFAULT_GENDER = 'NEUTRAL'
|
||||||
]
|
|
||||||
DEFAULT_GENDER = SUPPORTED_GENDERS[0]
|
|
||||||
GENDERS_DICT = {
|
|
||||||
'Neutral': texttospeech.enums.SsmlVoiceGender.NEUTRAL,
|
|
||||||
'Female': texttospeech.enums.SsmlVoiceGender.FEMALE,
|
|
||||||
'Male': texttospeech.enums.SsmlVoiceGender.MALE,
|
|
||||||
}
|
|
||||||
|
|
||||||
SUPPORTED_ENCODINGS = [
|
SUPPORTED_ENCODINGS = ['OGG_OPUS', 'MP3', 'LINEAR16']
|
||||||
'ogg', 'mp3', 'wav',
|
DEFAULT_ENCODING = 'OGG_OPUS'
|
||||||
]
|
|
||||||
DEFAULT_ENCODING = SUPPORTED_ENCODINGS[0]
|
|
||||||
ENCODINGS_DICT = {
|
|
||||||
'ogg': texttospeech.enums.AudioEncoding.OGG_OPUS,
|
|
||||||
'mp3': texttospeech.enums.AudioEncoding.MP3,
|
|
||||||
'wav': texttospeech.enums.AudioEncoding.LINEAR16,
|
|
||||||
}
|
|
||||||
|
|
||||||
MIN_SPEED = 0.25
|
MIN_SPEED = 0.25
|
||||||
MAX_SPEED = 4.0
|
MAX_SPEED = 4.0
|
||||||
@@ -56,75 +44,79 @@ MIN_GAIN = -96.0
|
|||||||
MAX_GAIN = 16.0
|
MAX_GAIN = 16.0
|
||||||
DEFAULT_GAIN = 0
|
DEFAULT_GAIN = 0
|
||||||
|
|
||||||
|
SUPPORTED_PROFILES = [
|
||||||
|
"wearable-class-device", "handset-class-device", "headphone-class-device",
|
||||||
|
"small-bluetooth-speaker-class-device",
|
||||||
|
"medium-bluetooth-speaker-class-device",
|
||||||
|
"large-home-entertainment-class-device",
|
||||||
|
"large-automotive-class-device", "telephony-class-application",
|
||||||
|
]
|
||||||
|
|
||||||
SUPPORTED_OPTIONS = [
|
SUPPORTED_OPTIONS = [
|
||||||
CONF_VOICE, CONF_GENDER, CONF_ENCODING, CONF_SPEED, CONF_PITCH, CONF_GAIN,
|
CONF_VOICE, CONF_GENDER, CONF_ENCODING, CONF_SPEED, CONF_PITCH, CONF_GAIN,
|
||||||
|
CONF_PROFILES,
|
||||||
]
|
]
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORTED_LANGUAGES),
|
vol.Optional(CONF_KEY_FILE): cv.string,
|
||||||
|
vol.Optional(CONF_LANG, default=DEFAULT_LANG):
|
||||||
|
vol.In(SUPPORTED_LANGUAGES),
|
||||||
vol.Optional(CONF_GENDER, default=DEFAULT_GENDER):
|
vol.Optional(CONF_GENDER, default=DEFAULT_GENDER):
|
||||||
vol.In(SUPPORTED_GENDERS),
|
vol.All(vol.Upper, vol.In(SUPPORTED_GENDERS)),
|
||||||
vol.Optional(CONF_VOICE, default=''): cv.string,
|
vol.Optional(CONF_VOICE): cv.string,
|
||||||
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
|
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING):
|
||||||
|
vol.All(vol.Upper, vol.In(SUPPORTED_ENCODINGS)),
|
||||||
vol.Optional(CONF_SPEED, default=DEFAULT_SPEED):
|
vol.Optional(CONF_SPEED, default=DEFAULT_SPEED):
|
||||||
vol.Range(min=MIN_SPEED, max=MAX_SPEED),
|
vol.Range(min=MIN_SPEED, max=MAX_SPEED),
|
||||||
vol.Optional(CONF_PITCH, default=DEFAULT_PITCH):
|
vol.Optional(CONF_PITCH, default=DEFAULT_PITCH):
|
||||||
vol.Range(min=MIN_PITCH, max=MAX_PITCH),
|
vol.Range(min=MIN_PITCH, max=MAX_PITCH),
|
||||||
vol.Optional(CONF_GAIN, default=DEFAULT_GAIN):
|
vol.Optional(CONF_GAIN, default=DEFAULT_GAIN):
|
||||||
vol.Range(min=MIN_GAIN, max=MAX_GAIN),
|
vol.Range(min=MIN_GAIN, max=MAX_GAIN),
|
||||||
vol.Optional(CONF_KEY_FILE, default=''): cv.string,
|
vol.Optional(CONF_PROFILES, default=[]):
|
||||||
|
vol.All(cv.ensure_list, [vol.In(SUPPORTED_PROFILES)]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
async def async_get_engine(hass, config):
|
async def async_get_engine(hass, config):
|
||||||
"""Set up Google Cloud TTS component."""
|
"""Set up Google Cloud TTS component."""
|
||||||
key_file = config[CONF_KEY_FILE]
|
key_file = config.get(CONF_KEY_FILE)
|
||||||
if key_file:
|
if key_file:
|
||||||
key_file = hass.config.path(key_file)
|
key_file = hass.config.path(key_file)
|
||||||
if not os.path.isfile(key_file):
|
if not os.path.isfile(key_file):
|
||||||
_LOGGER.error(
|
_LOGGER.error("%s doesn't exist", key_file)
|
||||||
"GOOGLE_APPLICATION_CREDENTIALS file doesn't exist!"
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return GoogleCloudTTSProvider(
|
return GoogleCloudTTSProvider(
|
||||||
hass,
|
hass,
|
||||||
key_file,
|
key_file,
|
||||||
config[CONF_LANG],
|
config.get(CONF_LANG),
|
||||||
config[CONF_GENDER],
|
config.get(CONF_GENDER),
|
||||||
config[CONF_VOICE],
|
config.get(CONF_VOICE),
|
||||||
config[CONF_ENCODING],
|
config.get(CONF_ENCODING),
|
||||||
config[CONF_SPEED],
|
config.get(CONF_SPEED),
|
||||||
config[CONF_PITCH],
|
config.get(CONF_PITCH),
|
||||||
config[CONF_GAIN]
|
config.get(CONF_GAIN),
|
||||||
|
config.get(CONF_PROFILES)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class GoogleCloudTTSProvider(Provider):
|
class GoogleCloudTTSProvider(Provider):
|
||||||
"""The Google Cloud TTS API provider."""
|
"""The Google Cloud TTS API provider."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, hass, key_file, language, gender, voice, encoding, speed,
|
||||||
hass,
|
pitch, gain, profiles
|
||||||
key_file,
|
|
||||||
lang,
|
|
||||||
gender,
|
|
||||||
voice,
|
|
||||||
encoding,
|
|
||||||
speed,
|
|
||||||
pitch,
|
|
||||||
gain
|
|
||||||
):
|
):
|
||||||
"""Init Google Cloud TTS service."""
|
"""Init Google Cloud TTS service."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.name = 'Google Cloud TTS'
|
self.name = 'Google Cloud TTS'
|
||||||
self._lang = lang
|
self._language = language
|
||||||
self._gender = gender
|
self._gender = gender
|
||||||
self._voice = voice
|
self._voice = voice
|
||||||
self._encoding = encoding
|
self._encoding = encoding
|
||||||
self._speed = speed
|
self._speed = speed
|
||||||
self._pitch = pitch
|
self._pitch = pitch
|
||||||
self._gain = gain
|
self._gain = gain
|
||||||
|
self._profiles = profiles
|
||||||
|
|
||||||
if key_file:
|
if key_file:
|
||||||
self._client = texttospeech \
|
self._client = texttospeech \
|
||||||
@@ -132,70 +124,67 @@ class GoogleCloudTTSProvider(Provider):
|
|||||||
else:
|
else:
|
||||||
self._client = texttospeech.TextToSpeechClient()
|
self._client = texttospeech.TextToSpeechClient()
|
||||||
|
|
||||||
@property
|
|
||||||
def default_language(self):
|
|
||||||
"""Return the default language."""
|
|
||||||
return self._lang
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_languages(self):
|
def supported_languages(self):
|
||||||
"""Return list of supported languages."""
|
"""Return list of supported languages."""
|
||||||
return SUPPORTED_LANGUAGES
|
return SUPPORTED_LANGUAGES
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_language(self):
|
||||||
|
"""Return the default language."""
|
||||||
|
return self._language
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_options(self):
|
def supported_options(self):
|
||||||
"""Return a list of supported options."""
|
"""Return a list of supported options."""
|
||||||
return SUPPORTED_OPTIONS
|
return SUPPORTED_OPTIONS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_options(self):
|
||||||
|
"""Return a dict including default options."""
|
||||||
|
return {
|
||||||
|
CONF_GENDER: self._gender,
|
||||||
|
CONF_VOICE: self._voice,
|
||||||
|
CONF_ENCODING: self._encoding,
|
||||||
|
CONF_SPEED: self._speed,
|
||||||
|
CONF_PITCH: self._pitch,
|
||||||
|
CONF_GAIN: self._gain,
|
||||||
|
CONF_PROFILES: self._profiles
|
||||||
|
}
|
||||||
|
|
||||||
async def async_get_tts_audio(self, message, language, options=None):
|
async def async_get_tts_audio(self, message, language, options=None):
|
||||||
"""Load TTS from google."""
|
"""Load TTS from google."""
|
||||||
|
_gender = options.get(CONF_GENDER).upper()
|
||||||
|
if _gender not in SUPPORTED_GENDERS: _gender = self._gender
|
||||||
|
_voice = options.get(CONF_VOICE) or self._voice
|
||||||
|
if _voice and not _voice.startswith(language): language = _voice[:5]
|
||||||
|
_encoding = options.get(CONF_ENCODING).upper()
|
||||||
|
if _encoding not in SUPPORTED_ENCODINGS: _encoding = self._encoding
|
||||||
|
_speed = options.get(CONF_SPEED)
|
||||||
|
_pitch = options.get(CONF_PITCH)
|
||||||
|
_gain = options.get(CONF_GAIN)
|
||||||
|
_profiles = options.get(CONF_PROFILES)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
synthesis_input = texttospeech.types.SynthesisInput(
|
||||||
|
text=message
|
||||||
|
) # pylint: disable=no-member
|
||||||
|
|
||||||
|
voice = texttospeech.types.VoiceSelectionParams(
|
||||||
|
language_code=language,
|
||||||
|
ssml_gender=texttospeech.enums.SsmlVoiceGender[_gender],
|
||||||
|
name=_voice
|
||||||
|
) # pylint: disable=no-member
|
||||||
|
|
||||||
|
audio_config = texttospeech.types.AudioConfig(
|
||||||
|
audio_encoding=texttospeech.enums.AudioEncoding[_encoding],
|
||||||
|
speaking_rate=max(min(_speed , MAX_SPEED), MIN_SPEED),
|
||||||
|
pitch=max(min(_pitch, MAX_PITCH), MIN_PITCH),
|
||||||
|
volume_gain_db=max(min(_gain, MAX_GAIN), MIN_GAIN),
|
||||||
|
effects_profile_id=_profiles,
|
||||||
|
) # pylint: disable=no-member
|
||||||
|
|
||||||
with async_timeout.timeout(10, loop=self.hass.loop):
|
with async_timeout.timeout(10, loop=self.hass.loop):
|
||||||
_language = language or self._lang
|
|
||||||
_gender = self._gender
|
|
||||||
_voice = self._voice
|
|
||||||
_encoding = self._encoding
|
|
||||||
_speed = self._speed
|
|
||||||
_pitch = self._pitch
|
|
||||||
_gain = self._gain
|
|
||||||
|
|
||||||
if options:
|
|
||||||
if CONF_GENDER in options:
|
|
||||||
_gender = options[CONF_GENDER].lower().capitalize()
|
|
||||||
if CONF_VOICE in options:
|
|
||||||
_voice = options[CONF_VOICE]
|
|
||||||
if CONF_ENCODING in options:
|
|
||||||
_encoding = options[CONF_ENCODING].lower()
|
|
||||||
if CONF_SPEED in options:
|
|
||||||
_speed = options[CONF_SPEED]
|
|
||||||
if CONF_PITCH in options:
|
|
||||||
_pitch = options[CONF_PITCH]
|
|
||||||
if CONF_GAIN in options:
|
|
||||||
_gain = options[CONF_GAIN]
|
|
||||||
|
|
||||||
synthesis_input = texttospeech.types.SynthesisInput(
|
|
||||||
text=message
|
|
||||||
) # pylint: disable=no-member
|
|
||||||
|
|
||||||
voice = texttospeech.types.VoiceSelectionParams(
|
|
||||||
language_code=_language,
|
|
||||||
ssml_gender=GENDERS_DICT.get(
|
|
||||||
_gender,
|
|
||||||
DEFAULT_GENDER
|
|
||||||
),
|
|
||||||
name=_voice
|
|
||||||
) # pylint: disable=no-member
|
|
||||||
|
|
||||||
audio_config = texttospeech.types.AudioConfig(
|
|
||||||
audio_encoding=ENCODINGS_DICT.get(
|
|
||||||
_encoding,
|
|
||||||
DEFAULT_ENCODING
|
|
||||||
),
|
|
||||||
speaking_rate=_speed,
|
|
||||||
pitch=_pitch,
|
|
||||||
volume_gain_db=_gain
|
|
||||||
) # pylint: disable=no-member
|
|
||||||
|
|
||||||
response = await self.hass.async_add_executor_job(
|
response = await self.hass.async_add_executor_job(
|
||||||
self._client.synthesize_speech,
|
self._client.synthesize_speech,
|
||||||
synthesis_input,
|
synthesis_input,
|
||||||
|
Reference in New Issue
Block a user