This commit is contained in:
lufton
2019-05-05 16:59:33 +03:00
parent d116e8a487
commit 40cfc8e752

View File

@@ -27,12 +27,11 @@ SUPPORTED_LANGUAGES = [
] ]
DEFAULT_LANG = 'en-US' DEFAULT_LANG = 'en-US'
SUPPORTED_GENDERS = ['NEUTRAL', 'FEMALE', 'MALE']
DEFAULT_GENDER = 'NEUTRAL' DEFAULT_GENDER = 'NEUTRAL'
VOICE_REGEX = r'[a-z]{2}-[A-Z]{2}-(Standard|Wavenet)-[A-Z]' VOICE_REGEX = r'[a-z]{2}-[A-Z]{2}-(Standard|Wavenet)-[A-Z]|'
DEFAULT_VOICE = ''
SUPPORTED_ENCODINGS = ['OGG_OPUS', 'MP3', 'LINEAR16']
DEFAULT_ENCODING = 'OGG_OPUS' DEFAULT_ENCODING = 'OGG_OPUS'
MIN_SPEED = 0.25 MIN_SPEED = 0.25
@@ -48,35 +47,46 @@ MAX_GAIN = 16.0
DEFAULT_GAIN = 0 DEFAULT_GAIN = 0
SUPPORTED_PROFILES = [ SUPPORTED_PROFILES = [
"wearable-class-device", "handset-class-device", "headphone-class-device", "wearable-class-device",
"handset-class-device",
"headphone-class-device",
"small-bluetooth-speaker-class-device", "small-bluetooth-speaker-class-device",
"medium-bluetooth-speaker-class-device", "medium-bluetooth-speaker-class-device",
"large-home-entertainment-class-device", "large-home-entertainment-class-device",
"large-automotive-class-device", "telephony-class-application", "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, CONF_PROFILES,
] ]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_KEY_FILE): vol.Optional(CONF_KEY_FILE): cv.string,
cv.string,
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.Optional(CONF_LANG, default=DEFAULT_LANG):
vol.In(SUPPORTED_LANGUAGES), vol.In(SUPPORTED_LANGUAGES),
vol.Optional(CONF_GENDER, default=DEFAULT_GENDER): vol.Optional(CONF_GENDER, default=DEFAULT_GENDER):
vol.All(vol.Upper, vol.In(SUPPORTED_GENDERS)), vol.All(vol.Upper, vol.In(
vol.Optional(CONF_VOICE): texttospeech.enums.SsmlVoiceGender.__members__
vol.All(cv.string, vol.Match(VOICE_REGEX)), )),
vol.Optional(CONF_VOICE, default=DEFAULT_VOICE):
cv.matches_regex(VOICE_REGEX),
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING):
vol.All(vol.Upper, vol.In(SUPPORTED_ENCODINGS)), vol.All(vol.Upper, vol.In(
texttospeech.enums.AudioEncoding.__members__
)),
vol.Optional(CONF_SPEED, default=DEFAULT_SPEED): vol.Optional(CONF_SPEED, default=DEFAULT_SPEED):
vol.Clamp(min=MIN_SPEED, max=MAX_SPEED), vol.All(vol.Coerce(float), vol.Clamp(min=MIN_SPEED, max=MAX_SPEED)),
vol.Optional(CONF_PITCH, default=DEFAULT_PITCH): vol.Optional(CONF_PITCH, default=DEFAULT_PITCH):
vol.Clamp(min=MIN_PITCH, max=MAX_PITCH), vol.All(vol.Coerce(float), vol.Clamp(min=MIN_PITCH, max=MAX_PITCH)),
vol.Optional(CONF_GAIN, default=DEFAULT_GAIN): vol.Optional(CONF_GAIN, default=DEFAULT_GAIN):
vol.Clamp(min=MIN_GAIN, max=MAX_GAIN), vol.All(vol.Coerce(float), vol.Clamp(min=MIN_GAIN, max=MAX_GAIN)),
vol.Optional(CONF_PROFILES, default=[]): vol.Optional(CONF_PROFILES, default=[]):
vol.All(cv.ensure_list, [vol.In(SUPPORTED_PROFILES)]), vol.All(cv.ensure_list, [vol.In(SUPPORTED_PROFILES)]),
}) })
@@ -88,7 +98,7 @@ async def async_get_engine(hass, config):
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("%s doesn't exist", key_file) _LOGGER.error("File %s doesn't exist", key_file)
return None return None
return GoogleCloudTTSProvider( return GoogleCloudTTSProvider(
@@ -96,7 +106,7 @@ async def async_get_engine(hass, config):
key_file, key_file,
config.get(CONF_LANG), config.get(CONF_LANG),
config.get(CONF_GENDER), config.get(CONF_GENDER),
config.get(CONF_VOICE, ''), config.get(CONF_VOICE),
config.get(CONF_ENCODING), config.get(CONF_ENCODING),
config.get(CONF_SPEED), config.get(CONF_SPEED),
config.get(CONF_PITCH), config.get(CONF_PITCH),
@@ -109,8 +119,17 @@ class GoogleCloudTTSProvider(Provider):
"""The Google Cloud TTS API provider.""" """The Google Cloud TTS API provider."""
def __init__( def __init__(
self, hass, key_file, language, gender, voice, encoding, speed, self,
pitch, gain, profiles hass,
key_file = None,
language=DEFAULT_LANG,
gender=DEFAULT_GENDER,
voice=DEFAULT_VOICE,
encoding=DEFAULT_ENCODING,
speed=1.0,
pitch=0,
gain=0,
profiles=[]
): ):
"""Init Google Cloud TTS service.""" """Init Google Cloud TTS service."""
self.hass = hass self.hass = hass
@@ -161,17 +180,17 @@ class GoogleCloudTTSProvider(Provider):
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() _gender = options.get(CONF_GENDER).upper()
if _gender not in SUPPORTED_GENDERS: if _gender not in texttospeech.enums.SsmlVoiceGender.__members__:
_gender = self._gender _gender = self._gender
_voice = options.get(CONF_VOICE) or self._voice _voice = options.get(CONF_VOICE) or self._voice
if not re.match(VOICE_REGEX, _voice): if not re.match(VOICE_REGEX, _voice):
_voice = self._voice _voice = self._voice
if not _voice.startswith(language): if _voice and not _voice.startswith(language):
language = _voice[:5] language = _voice[:5]
_encoding = options.get(CONF_ENCODING).upper() _encoding = options.get(CONF_ENCODING).upper()
if _encoding not in SUPPORTED_ENCODINGS: if _encoding not in texttospeech.enums.AudioEncoding.__members__:
_encoding = self._encoding _encoding = self._encoding
_speed = options.get(CONF_SPEED) _speed = options.get(CONF_SPEED)
@@ -180,15 +199,16 @@ class GoogleCloudTTSProvider(Provider):
_profiles = options.get(CONF_PROFILES) _profiles = options.get(CONF_PROFILES)
try: try:
# pylint: disable=no-member
synthesis_input = texttospeech.types.SynthesisInput( synthesis_input = texttospeech.types.SynthesisInput(
text=message text=message
) # pylint: disable=no-member )
voice = texttospeech.types.VoiceSelectionParams( voice = texttospeech.types.VoiceSelectionParams(
language_code=language, language_code=language,
ssml_gender=texttospeech.enums.SsmlVoiceGender[_gender], ssml_gender=texttospeech.enums.SsmlVoiceGender[_gender],
name=_voice name=_voice
) # pylint: disable=no-member )
audio_config = texttospeech.types.AudioConfig( audio_config = texttospeech.types.AudioConfig(
audio_encoding=texttospeech.enums.AudioEncoding[_encoding], audio_encoding=texttospeech.enums.AudioEncoding[_encoding],
@@ -196,7 +216,8 @@ class GoogleCloudTTSProvider(Provider):
pitch=max(min(_pitch, MAX_PITCH), MIN_PITCH), pitch=max(min(_pitch, MAX_PITCH), MIN_PITCH),
volume_gain_db=max(min(_gain, MAX_GAIN), MIN_GAIN), volume_gain_db=max(min(_gain, MAX_GAIN), MIN_GAIN),
effects_profile_id=_profiles, effects_profile_id=_profiles,
) # pylint: disable=no-member )
# pylint: enable=no-member
with async_timeout.timeout(10, loop=self.hass.loop): with async_timeout.timeout(10, loop=self.hass.loop):
response = await self.hass.async_add_executor_job( response = await self.hass.async_add_executor_job(