Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Amazon Polly generative engine #117320

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ac09793
update list of AWS regions
jschlyter May 12, 2024
c53ffbd
Update list of voices via:
jschlyter May 12, 2024
703e31d
add generative and long-form engines
jschlyter May 12, 2024
7caa167
allow setting engine and region in Amazon Polly options
jschlyter May 12, 2024
7f93611
region not possible in options
jschlyter May 12, 2024
750aef8
add dictionary of per engine voice_id and allow engine in options
jschlyter May 12, 2024
6342c6c
reformat
jschlyter May 12, 2024
705bfbb
Merge branch 'dev' into polly_generative
jschlyter May 12, 2024
31152a8
add default engine to default options
jschlyter May 12, 2024
249245c
sort import
jschlyter May 12, 2024
63af4c6
move statement
jschlyter May 13, 2024
1199f43
update list of voices using voices.py script
jschlyter May 13, 2024
8a47c99
also parse AdditionalLanguageCodes
jschlyter May 13, 2024
864056c
please ruff
jschlyter May 13, 2024
ab1ae33
lint
jschlyter May 13, 2024
f02289d
generate SUPPORTED_REGIONS and SUPPORTED_VOICES using script
jschlyter May 13, 2024
be37c27
sort voices
jschlyter May 13, 2024
60eb69d
remove typo
jschlyter May 13, 2024
112b556
use sets where possible
jschlyter May 14, 2024
ee4d91d
Merge branch 'dev' into polly_generative
jschlyter May 19, 2024
cf24611
Merge branch 'dev' into polly_generative
jschlyter May 22, 2024
eca91c0
add Final
jschlyter May 22, 2024
56c9bc8
regen
jschlyter May 22, 2024
3656403
Merge branch 'dev' into polly_generative
jschlyter May 25, 2024
2e9a2d8
Merge branch 'dev' into polly_generative
jschlyter May 30, 2024
2784e8a
Merge branch 'dev' into polly_generative
jschlyter Jun 9, 2024
c72423a
Automatically generate list of voices and regions. Requires AWS crede…
jschlyter Jun 9, 2024
5cf055e
add missing commit
jschlyter Jun 9, 2024
761f2c8
replace pydantic with dataclass
jschlyter Jun 9, 2024
12618bc
dictionary values are strings or list of strings
jschlyter Jun 9, 2024
f560b3a
Merge branch 'amazon_polly_generated' into polly_generative
jschlyter Jun 9, 2024
5b05a80
also generated set of supported engines
jschlyter Jun 9, 2024
a71d8d5
Merge branch 'dev' into amazon_polly_generated
jschlyter Jun 9, 2024
e61e011
use sets for amazon polly parameters
jschlyter Jun 9, 2024
548ad6e
move default for readability
jschlyter Jun 14, 2024
f70cb63
Merge branch 'amazon_polly_generated' into polly_generative
jschlyter Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 9 additions & 112 deletions homeassistant/components/amazon_polly/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,135 +8,32 @@
CONF_ACCESS_KEY_ID: Final = "aws_access_key_id"
CONF_SECRET_ACCESS_KEY: Final = "aws_secret_access_key"

DEFAULT_REGION: Final = "us-east-1"
SUPPORTED_REGIONS: Final[list[str]] = [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"ca-central-1",
"eu-west-1",
"eu-central-1",
"eu-west-2",
"eu-west-3",
"ap-southeast-1",
"ap-southeast-2",
"ap-northeast-2",
"ap-northeast-1",
"ap-south-1",
"sa-east-1",
]

CONF_ENGINE: Final = "engine"
CONF_VOICE: Final = "voice"
CONF_OUTPUT_FORMAT: Final = "output_format"
CONF_SAMPLE_RATE: Final = "sample_rate"
CONF_TEXT_TYPE: Final = "text_type"

SUPPORTED_VOICES: Final[list[str]] = [
"Aditi", # Hindi
"Amy", # English (British)
"Aria", # English (New Zealand), Neural
"Arlet", # Catalan, Neural
"Arthur", # English, Neural
"Astrid", # Swedish
"Ayanda", # English (South African), Neural
"Bianca", # Italian
"Brian", # English (British)
"Camila", # Portuguese, Brazilian
"Carla", # Italian
"Carmen", # Romanian
"Celine", # French
"Chantal", # French Canadian
"Conchita", # Spanish (European)
"Cristiano", # Portuguese (European)
"Daniel", # German, Neural
"Dora", # Icelandic
"Elin", # Swedish, Neural
"Emma", # English
"Enrique", # Spanish (European)
"Ewa", # Polish
"Filiz", # Turkish
"Gabrielle", # French (Canadian)
"Geraint", # English Welsh
"Giorgio", # Italian
"Gwyneth", # Welsh
"Hala", # Arabic (Gulf), Neural
"Hannah", # German (Austrian), Neural
"Hans", # German
"Hiujin", # Chinese (Cantonese), Neural
"Ida", # Norwegian, Neural
"Ines", # Portuguese, European # codespell:ignore ines
"Ivy", # English
"Jacek", # Polish
"Jan", # Polish
"Joanna", # English
"Joey", # English
"Justin", # English
"Kajal", # English (Indian)/Hindi (Bilingual ), Neural
"Karl", # Icelandic
"Kendra", # English
"Kevin", # English, Neural
"Kimberly", # English
"Laura", # Dutch, Neural
"Lea", # French
"Liam", # Canadian French, Neural
"Liv", # Norwegian
"Lotte", # Dutch
"Lucia", # Spanish European
"Lupe", # Spanish US
"Mads", # Danish
"Maja", # Polish
"Marlene", # German
"Mathieu", # French
"Matthew", # English
"Maxim", # Russian
"Mia", # Spanish Mexican
"Miguel", # Spanish US
"Mizuki", # Japanese
"Naja", # Danish
"Nicole", # English Australian
"Ola", # Polish, Neural
"Olivia", # Female, Australian, Neural
"Penelope", # Spanish US
"Pedro", # Spanish US, Neural
"Raveena", # English, Indian
"Ricardo", # Portuguese (Brazilian)
"Ruben", # Dutch
"Russell", # English (Australian)
"Ruth", # English, Neural
"Salli", # English
"Seoyeon", # Korean
"Stephen", # English, Neural
"Suvi", # Finnish
"Takumi", # Japanese
"Tatyana", # Russian
"Vicki", # German
"Vitoria", # Portuguese, Brazilian
"Zeina", # Arabic
"Zhiyu", # Chinese
]

SUPPORTED_OUTPUT_FORMATS: Final[list[str]] = ["mp3", "ogg_vorbis", "pcm"]
SUPPORTED_OUTPUT_FORMATS: Final[set[str]] = {"mp3", "ogg_vorbis", "pcm"}

SUPPORTED_ENGINES: Final[list[str]] = ["neural", "standard"]
SUPPORTED_SAMPLE_RATES: Final[set[str]] = {"8000", "16000", "22050", "24000"}

SUPPORTED_SAMPLE_RATES: Final[list[str]] = ["8000", "16000", "22050", "24000"]

SUPPORTED_SAMPLE_RATES_MAP: Final[dict[str, list[str]]] = {
"mp3": ["8000", "16000", "22050", "24000"],
"ogg_vorbis": ["8000", "16000", "22050"],
"pcm": ["8000", "16000"],
SUPPORTED_SAMPLE_RATES_MAP: Final[dict[str, set[str]]] = {
"mp3": {"8000", "16000", "22050", "24000"},
"ogg_vorbis": {"8000", "16000", "22050"},
"pcm": {"8000", "16000"},
}

SUPPORTED_TEXT_TYPES: Final[list[str]] = ["text", "ssml"]
SUPPORTED_TEXT_TYPES: Final[set[str]] = {"text", "ssml"}

CONTENT_TYPE_EXTENSIONS: Final[dict[str, str]] = {
"audio/mpeg": "mp3",
"audio/ogg": "ogg",
"audio/pcm": "pcm",
}

DEFAULT_REGION: Final = "us-east-1"

DEFAULT_ENGINE: Final = "standard"
DEFAULT_VOICE: Final = "Joanna"
DEFAULT_OUTPUT_FORMAT: Final = "mp3"
Expand Down
31 changes: 24 additions & 7 deletions homeassistant/components/amazon_polly/tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from collections import defaultdict
import logging
from typing import Any, Final

Expand All @@ -16,6 +17,11 @@
)
from homeassistant.const import ATTR_CREDENTIALS, CONF_PROFILE_NAME
from homeassistant.core import HomeAssistant
from homeassistant.generated.amazon_polly import (
SUPPORTED_ENGINES,
SUPPORTED_REGIONS,
SUPPORTED_VOICES,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

Expand All @@ -38,13 +44,10 @@
DEFAULT_SAMPLE_RATES,
DEFAULT_TEXT_TYPE,
DEFAULT_VOICE,
SUPPORTED_ENGINES,
SUPPORTED_OUTPUT_FORMATS,
SUPPORTED_REGIONS,
SUPPORTED_SAMPLE_RATES,
SUPPORTED_SAMPLE_RATES_MAP,
SUPPORTED_TEXT_TYPES,
SUPPORTED_VOICES,
)

_LOGGER: Final = logging.getLogger(__name__)
Expand Down Expand Up @@ -112,6 +115,8 @@ def get_engine(

all_voices: dict[str, dict[str, str]] = {}

all_engines: dict[str, set[str]] = defaultdict(set)

all_voices_req = polly_client.describe_voices()

for voice in all_voices_req.get("Voices", []):
Expand All @@ -122,8 +127,12 @@ def get_engine(
language_code: str | None = voice.get("LanguageCode")
if language_code is not None and language_code not in supported_languages:
supported_languages.append(language_code)
for engine in voice.get("SupportedEngines"):
all_engines[engine].add(voice_id)

return AmazonPollyProvider(polly_client, config, supported_languages, all_voices)
return AmazonPollyProvider(
polly_client, config, supported_languages, all_voices, all_engines
)


class AmazonPollyProvider(Provider):
Expand All @@ -135,13 +144,16 @@ def __init__(
config: ConfigType,
supported_languages: list[str],
all_voices: dict[str, dict[str, str]],
all_engines: dict[str, set[str]],
) -> None:
"""Initialize Amazon Polly provider for TTS."""
self.client = polly_client
self.config = config
self.supported_langs = supported_languages
self.all_voices = all_voices
self.all_engines = all_engines
self.default_voice: str = self.config[CONF_VOICE]
self.default_engine: str = self.config[CONF_ENGINE]
self.name = "Amazon Polly"

@property
Expand All @@ -157,12 +169,12 @@ def default_language(self) -> str | None:
@property
def default_options(self) -> dict[str, str]:
"""Return dict include default options."""
return {CONF_VOICE: self.default_voice}
return {CONF_VOICE: self.default_voice, CONF_ENGINE: self.default_engine}

@property
def supported_options(self) -> list[str]:
"""Return a list of supported options."""
return [CONF_VOICE]
return [CONF_VOICE, CONF_ENGINE]

def get_tts_audio(
self,
Expand All @@ -177,9 +189,14 @@ def get_tts_audio(
_LOGGER.error("%s does not support the %s language", voice_id, language)
return None, None

engine = options.get(CONF_ENGINE, self.default_engine)
if voice_id not in self.all_engines[engine]:
_LOGGER.error("%s does not support the %s engine", voice_id, engine)
return None, None

_LOGGER.debug("Requesting TTS file for text: %s", message)
resp = self.client.synthesize_speech(
Engine=self.config[CONF_ENGINE],
Engine=engine,
OutputFormat=self.config[CONF_OUTPUT_FORMAT],
SampleRate=self.config[CONF_SAMPLE_RATE],
Text=message,
Expand Down
137 changes: 137 additions & 0 deletions homeassistant/generated/amazon_polly.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Automatically generated file.

To update, run python3 -m script.amazon_polly
"""

from __future__ import annotations

from typing import Final

SUPPORTED_ENGINES: Final[set[str]] = {
"generative",
"long-form",
"neural",
"standard",
}

SUPPORTED_REGIONS: Final[set[str]] = {
"af-south-1",
"ap-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-north-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"me-south-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
}

SUPPORTED_VOICES: Final[set[str]] = {
"Aditi",
"Adriano",
"Amy",
"Andres",
"Aria",
"Arlet",
"Arthur",
"Astrid",
"Ayanda",
"Bianca",
"Brian",
"Burcu",
"Camila",
"Carla",
"Carmen",
"Celine",
"Chantal",
"Conchita",
"Cristiano",
"Daniel",
"Danielle",
"Dora",
"Elin",
"Emma",
"Enrique",
"Ewa",
"Filiz",
"Gabrielle",
"Geraint",
"Giorgio",
"Gregory",
"Gwyneth",
"Hala",
"Hannah",
"Hans",
"Hiujin",
"Ida",
"Ines",
"Isabelle",
"Ivy",
"Jacek",
"Jan",
"Joanna",
"Joey",
"Justin",
"Kajal",
"Karl",
"Kazuha",
"Kendra",
"Kevin",
"Kimberly",
"Laura",
"Lea",
"Liam",
"Lisa",
"Liv",
"Lotte",
"Lucia",
"Lupe",
"Mads",
"Maja",
"Marlene",
"Mathieu",
"Matthew",
"Maxim",
"Mia",
"Miguel",
"Mizuki",
"Naja",
"Niamh",
"Nicole",
"Ola",
"Olivia",
"Pedro",
"Penelope",
"Raveena",
"Remi",
"Ricardo",
"Ruben",
"Russell",
"Ruth",
"Salli",
"Seoyeon",
"Sergio",
"Sofie",
"Stephen",
"Suvi",
"Takumi",
"Tatyana",
"Thiago",
"Tomoko",
"Vicki",
"Vitoria",
"Zayd",
"Zeina",
"Zhiyu",
}
Loading