From 2a1d81b407a0ae0c54627d2971462db44766c61e Mon Sep 17 00:00:00 2001 From: lufton Date: Sun, 5 May 2019 13:39:20 +0300 Subject: [PATCH] * Added profiles parameter * Changed supported languages and encodings values * Added parameters validations --- .../components/google_cloud/manifest.json | 2 +- homeassistant/components/google_cloud/tts.py | 187 +++++++++--------- 2 files changed, 89 insertions(+), 100 deletions(-) diff --git a/homeassistant/components/google_cloud/manifest.json b/homeassistant/components/google_cloud/manifest.json index dd8e19dde03..c8ac0d2e81e 100644 --- a/homeassistant/components/google_cloud/manifest.json +++ b/homeassistant/components/google_cloud/manifest.json @@ -1,6 +1,6 @@ { "domain": "google_cloud", - "name": "Google Cloud TTS", + "name": "Google Cloud Platform", "documentation": "https://www.home-assistant.io/components/google_cloud", "requirements": [ "google-cloud-texttospeech==0.4.0" diff --git a/homeassistant/components/google_cloud/tts.py b/homeassistant/components/google_cloud/tts.py index 599b3b96d75..43187822827 100644 --- a/homeassistant/components/google_cloud/tts.py +++ b/homeassistant/components/google_cloud/tts.py @@ -17,32 +17,20 @@ CONF_ENCODING = 'encoding' CONF_SPEED = 'speed' CONF_PITCH = 'pitch' CONF_GAIN = 'gain' +CONF_PROFILES = 'profiles' SUPPORTED_LANGUAGES = [ - 'en', 'da', 'nl', 'fr', 'de', 'it', 'ja', 'ko', 'nb', - 'pl', 'pt', 'ru', 'sk', 'es', 'sv', 'tr', 'uk', + 'da-DK', 'de-DE', 'en-AU', 'en-GB', 'en-US', 'es-ES', 'fr-CA', 'fr-FR', + '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 = [ - 'Neutral', 'Female', 'Male', -] -DEFAULT_GENDER = SUPPORTED_GENDERS[0] -GENDERS_DICT = { - 'Neutral': texttospeech.enums.SsmlVoiceGender.NEUTRAL, - 'Female': texttospeech.enums.SsmlVoiceGender.FEMALE, - 'Male': texttospeech.enums.SsmlVoiceGender.MALE, -} +SUPPORTED_GENDERS = ['NEUTRAL', 'FEMALE', 'MALE'] +DEFAULT_GENDER = 'NEUTRAL' -SUPPORTED_ENCODINGS = [ - 'ogg', 'mp3', 'wav', -] -DEFAULT_ENCODING = SUPPORTED_ENCODINGS[0] -ENCODINGS_DICT = { - 'ogg': texttospeech.enums.AudioEncoding.OGG_OPUS, - 'mp3': texttospeech.enums.AudioEncoding.MP3, - 'wav': texttospeech.enums.AudioEncoding.LINEAR16, -} +SUPPORTED_ENCODINGS = ['OGG_OPUS', 'MP3', 'LINEAR16'] +DEFAULT_ENCODING = 'OGG_OPUS' MIN_SPEED = 0.25 MAX_SPEED = 4.0 @@ -56,75 +44,79 @@ MIN_GAIN = -96.0 MAX_GAIN = 16.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 = [ CONF_VOICE, CONF_GENDER, CONF_ENCODING, CONF_SPEED, CONF_PITCH, CONF_GAIN, + CONF_PROFILES, ] 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.In(SUPPORTED_GENDERS), - vol.Optional(CONF_VOICE, default=''): cv.string, - vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, + vol.All(vol.Upper, vol.In(SUPPORTED_GENDERS)), + vol.Optional(CONF_VOICE): 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.Range(min=MIN_SPEED, max=MAX_SPEED), vol.Optional(CONF_PITCH, default=DEFAULT_PITCH): vol.Range(min=MIN_PITCH, max=MAX_PITCH), vol.Optional(CONF_GAIN, default=DEFAULT_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): """Set up Google Cloud TTS component.""" - key_file = config[CONF_KEY_FILE] + key_file = config.get(CONF_KEY_FILE) if key_file: key_file = hass.config.path(key_file) if not os.path.isfile(key_file): - _LOGGER.error( - "GOOGLE_APPLICATION_CREDENTIALS file doesn't exist!" - ) + _LOGGER.error("%s doesn't exist", key_file) return None return GoogleCloudTTSProvider( hass, key_file, - config[CONF_LANG], - config[CONF_GENDER], - config[CONF_VOICE], - config[CONF_ENCODING], - config[CONF_SPEED], - config[CONF_PITCH], - config[CONF_GAIN] + config.get(CONF_LANG), + config.get(CONF_GENDER), + config.get(CONF_VOICE), + config.get(CONF_ENCODING), + config.get(CONF_SPEED), + config.get(CONF_PITCH), + config.get(CONF_GAIN), + config.get(CONF_PROFILES) ) class GoogleCloudTTSProvider(Provider): """The Google Cloud TTS API provider.""" - def __init__( - self, - hass, - key_file, - lang, - gender, - voice, - encoding, - speed, - pitch, - gain + self, hass, key_file, language, gender, voice, encoding, speed, + pitch, gain, profiles ): """Init Google Cloud TTS service.""" self.hass = hass self.name = 'Google Cloud TTS' - self._lang = lang + self._language = language self._gender = gender self._voice = voice self._encoding = encoding self._speed = speed self._pitch = pitch self._gain = gain + self._profiles = profiles if key_file: self._client = texttospeech \ @@ -132,70 +124,67 @@ class GoogleCloudTTSProvider(Provider): else: self._client = texttospeech.TextToSpeechClient() - @property - def default_language(self): - """Return the default language.""" - return self._lang - @property def supported_languages(self): """Return list of supported languages.""" return SUPPORTED_LANGUAGES + @property + def default_language(self): + """Return the default language.""" + return self._language + @property def supported_options(self): """Return a list of 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): """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: + 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): - _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( self._client.synthesize_speech, synthesis_input,