Migrate google translate to config entries (#93803)
Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
22e1feb223
commit
f9366e5cc7
11 changed files with 557 additions and 103 deletions
|
@ -1 +1,20 @@
|
||||||
"""The google_translate component."""
|
"""The Google Translate text-to-speech integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [Platform.TTS]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Set up Google Translate text-to-speech from a config entry."""
|
||||||
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
|
49
homeassistant/components/google_translate/config_flow.py
Normal file
49
homeassistant/components/google_translate/config_flow.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
"""Config flow for Google Translate text-to-speech integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.tts import CONF_LANG
|
||||||
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CONF_TLD,
|
||||||
|
DEFAULT_LANG,
|
||||||
|
DEFAULT_TLD,
|
||||||
|
DOMAIN,
|
||||||
|
SUPPORT_LANGUAGES,
|
||||||
|
SUPPORT_TLD,
|
||||||
|
)
|
||||||
|
|
||||||
|
STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES),
|
||||||
|
vol.Optional(CONF_TLD, default=DEFAULT_TLD): vol.In(SUPPORT_TLD),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for Google Translate text-to-speech."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Handle the initial step."""
|
||||||
|
if user_input is not None:
|
||||||
|
self._async_abort_entries_match(
|
||||||
|
{
|
||||||
|
CONF_LANG: user_input[CONF_LANG],
|
||||||
|
CONF_TLD: user_input[CONF_TLD],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return self.async_create_entry(
|
||||||
|
title="Google Translate text-to-speech", data=user_input
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(step_id="user", data_schema=STEP_USER_DATA_SCHEMA)
|
|
@ -1,6 +1,11 @@
|
||||||
"""Constant for google_translate integration."""
|
"""Constants for the Google Translate text-to-speech integration."""
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
CONF_TLD = "tld"
|
||||||
|
DEFAULT_LANG = "en"
|
||||||
|
DEFAULT_TLD = "com"
|
||||||
|
DOMAIN = "google_translate"
|
||||||
|
|
||||||
SUPPORT_LANGUAGES = [
|
SUPPORT_LANGUAGES = [
|
||||||
"af",
|
"af",
|
||||||
"ar",
|
"ar",
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
"domain": "google_translate",
|
"domain": "google_translate",
|
||||||
"name": "Google Translate text-to-speech",
|
"name": "Google Translate text-to-speech",
|
||||||
"codeowners": [],
|
"codeowners": [],
|
||||||
|
"config_flow": true,
|
||||||
|
"dependencies": ["repairs"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/google_translate",
|
"documentation": "https://www.home-assistant.io/integrations/google_translate",
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["gtts"],
|
"loggers": ["gtts"],
|
||||||
|
|
15
homeassistant/components/google_translate/strings.json
Normal file
15
homeassistant/components/google_translate/strings.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"language": "Language",
|
||||||
|
"tld": "TLD"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,33 +1,120 @@
|
||||||
"""Support for the Google speech service."""
|
"""Support for the Google speech service."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from gtts import gTTS, gTTSError
|
from gtts import gTTS, gTTSError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.tts import CONF_LANG, PLATFORM_SCHEMA, Provider
|
from homeassistant.components.tts import (
|
||||||
|
CONF_LANG,
|
||||||
|
PLATFORM_SCHEMA,
|
||||||
|
Provider,
|
||||||
|
TextToSpeechEntity,
|
||||||
|
TtsAudioType,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
|
|
||||||
from .const import MAP_LANG_TLD, SUPPORT_LANGUAGES, SUPPORT_TLD
|
from .const import (
|
||||||
|
CONF_TLD,
|
||||||
|
DEFAULT_LANG,
|
||||||
|
DEFAULT_TLD,
|
||||||
|
MAP_LANG_TLD,
|
||||||
|
SUPPORT_LANGUAGES,
|
||||||
|
SUPPORT_TLD,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_LANG = "en"
|
|
||||||
|
|
||||||
SUPPORT_OPTIONS = ["tld"]
|
SUPPORT_OPTIONS = ["tld"]
|
||||||
|
|
||||||
DEFAULT_TLD = "com"
|
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES),
|
vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES),
|
||||||
vol.Optional("tld", default=DEFAULT_TLD): vol.In(SUPPORT_TLD),
|
vol.Optional(CONF_TLD, default=DEFAULT_TLD): vol.In(SUPPORT_TLD),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_get_engine(hass, config, discovery_info=None):
|
async def async_get_engine(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
|
) -> GoogleProvider:
|
||||||
"""Set up Google speech component."""
|
"""Set up Google speech component."""
|
||||||
return GoogleProvider(hass, config[CONF_LANG], config["tld"])
|
return GoogleProvider(hass, config[CONF_LANG], config[CONF_TLD])
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up Google Translate speech platform via config entry."""
|
||||||
|
default_language = config_entry.data[CONF_LANG]
|
||||||
|
default_tld = config_entry.data[CONF_TLD]
|
||||||
|
async_add_entities([GoogleTTSEntity(config_entry, default_language, default_tld)])
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleTTSEntity(TextToSpeechEntity):
|
||||||
|
"""The Google speech API entity."""
|
||||||
|
|
||||||
|
def __init__(self, config_entry: ConfigEntry, lang: str, tld: str) -> None:
|
||||||
|
"""Init Google TTS service."""
|
||||||
|
if lang in MAP_LANG_TLD:
|
||||||
|
self._lang = MAP_LANG_TLD[lang].lang
|
||||||
|
self._tld = MAP_LANG_TLD[lang].tld
|
||||||
|
else:
|
||||||
|
self._lang = lang
|
||||||
|
self._tld = tld
|
||||||
|
self._attr_name = f"Google {self._lang} {self._tld}"
|
||||||
|
self._attr_unique_id = config_entry.entry_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_language(self):
|
||||||
|
"""Return the default language."""
|
||||||
|
return self._lang
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_languages(self):
|
||||||
|
"""Return list of supported languages."""
|
||||||
|
return SUPPORT_LANGUAGES
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_options(self):
|
||||||
|
"""Return a list of supported options."""
|
||||||
|
return SUPPORT_OPTIONS
|
||||||
|
|
||||||
|
def get_tts_audio(
|
||||||
|
self, message: str, language: str, options: dict[str, Any] | None = None
|
||||||
|
) -> TtsAudioType:
|
||||||
|
"""Load TTS from google."""
|
||||||
|
tld = self._tld
|
||||||
|
if language in MAP_LANG_TLD:
|
||||||
|
tld_language = MAP_LANG_TLD[language]
|
||||||
|
tld = tld_language.tld
|
||||||
|
language = tld_language.lang
|
||||||
|
if options is not None and "tld" in options:
|
||||||
|
tld = options["tld"]
|
||||||
|
|
||||||
|
tts = gTTS(text=message, lang=language, tld=tld)
|
||||||
|
mp3_data = BytesIO()
|
||||||
|
|
||||||
|
try:
|
||||||
|
tts.write_to_fp(mp3_data)
|
||||||
|
except gTTSError as exc:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Error during processing of TTS request %s", exc, exc_info=True
|
||||||
|
)
|
||||||
|
raise HomeAssistantError(exc) from exc
|
||||||
|
|
||||||
|
return "mp3", mp3_data.getvalue()
|
||||||
|
|
||||||
|
|
||||||
class GoogleProvider(Provider):
|
class GoogleProvider(Provider):
|
||||||
|
|
|
@ -173,6 +173,7 @@ FLOWS = {
|
||||||
"google_generative_ai_conversation",
|
"google_generative_ai_conversation",
|
||||||
"google_mail",
|
"google_mail",
|
||||||
"google_sheets",
|
"google_sheets",
|
||||||
|
"google_translate",
|
||||||
"google_travel_time",
|
"google_travel_time",
|
||||||
"govee_ble",
|
"govee_ble",
|
||||||
"gpslogger",
|
"gpslogger",
|
||||||
|
|
|
@ -2084,7 +2084,7 @@
|
||||||
},
|
},
|
||||||
"google_translate": {
|
"google_translate": {
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"config_flow": false,
|
"config_flow": true,
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"name": "Google Translate text-to-speech"
|
"name": "Google Translate text-to-speech"
|
||||||
},
|
},
|
||||||
|
|
14
tests/components/google_translate/conftest.py
Normal file
14
tests/components/google_translate/conftest.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
"""Common fixtures for the Google Translate text-to-speech tests."""
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
"""Override async_setup_entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.google_translate.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
yield mock_setup_entry
|
68
tests/components/google_translate/test_config_flow.py
Normal file
68
tests/components/google_translate/test_config_flow.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
"""Test the Google Translate text-to-speech config flow."""
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components.google_translate.const import CONF_TLD, DOMAIN
|
||||||
|
from homeassistant.components.tts import CONF_LANG
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_step(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||||
|
"""Test user step create entry result."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == FlowResultType.FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_LANG: "de",
|
||||||
|
CONF_TLD: "de",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "Google Translate text-to-speech"
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_LANG: "de",
|
||||||
|
CONF_TLD: "de",
|
||||||
|
}
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_already_configured(
|
||||||
|
hass: HomeAssistant, mock_setup_entry: AsyncMock
|
||||||
|
) -> None:
|
||||||
|
"""Test user step already configured entry."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_LANG: "de", CONF_TLD: "de"}
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == FlowResultType.FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_LANG: "de",
|
||||||
|
CONF_TLD: "de",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 0
|
|
@ -1,21 +1,27 @@
|
||||||
"""The tests for the Google speech platform."""
|
"""The tests for the Google speech platform."""
|
||||||
from unittest.mock import patch
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from gtts import gTTSError
|
from gtts import gTTSError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import media_source, tts
|
from homeassistant.components import media_source, tts
|
||||||
|
from homeassistant.components.google_translate.const import CONF_TLD, DOMAIN
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
ATTR_MEDIA_CONTENT_ID,
|
ATTR_MEDIA_CONTENT_ID,
|
||||||
DOMAIN as DOMAIN_MP,
|
DOMAIN as DOMAIN_MP,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
)
|
)
|
||||||
from homeassistant.config import async_process_ha_core_config
|
from homeassistant.config import async_process_ha_core_config
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM
|
||||||
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import MockConfigEntry, async_mock_service
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
@ -29,7 +35,7 @@ def mock_tts_cache_dir_autouse(mock_tts_cache_dir):
|
||||||
return mock_tts_cache_dir
|
return mock_tts_cache_dir
|
||||||
|
|
||||||
|
|
||||||
async def get_media_source_url(hass, media_content_id):
|
async def get_media_source_url(hass: HomeAssistant, media_content_id: str) -> str:
|
||||||
"""Get the media source url."""
|
"""Get the media source url."""
|
||||||
if media_source.DOMAIN not in hass.config.components:
|
if media_source.DOMAIN not in hass.config.components:
|
||||||
assert await async_setup_component(hass, media_source.DOMAIN, {})
|
assert await async_setup_component(hass, media_source.DOMAIN, {})
|
||||||
|
@ -39,13 +45,13 @@ async def get_media_source_url(hass, media_content_id):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def calls(hass):
|
async def calls(hass: HomeAssistant) -> list[ServiceCall]:
|
||||||
"""Mock media player calls."""
|
"""Mock media player calls."""
|
||||||
return async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
return async_mock_service(hass, DOMAIN_MP, SERVICE_PLAY_MEDIA)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
async def setup_internal_url(hass):
|
async def setup_internal_url(hass: HomeAssistant) -> None:
|
||||||
"""Set up internal url."""
|
"""Set up internal url."""
|
||||||
await async_process_ha_core_config(
|
await async_process_ha_core_config(
|
||||||
hass, {"internal_url": "http://example.local:8123"}
|
hass, {"internal_url": "http://example.local:8123"}
|
||||||
|
@ -53,26 +59,85 @@ async def setup_internal_url(hass):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_gtts():
|
def mock_gtts() -> Generator[MagicMock, None, None]:
|
||||||
"""Mock gtts."""
|
"""Mock gtts."""
|
||||||
with patch("homeassistant.components.google_translate.tts.gTTS") as mock_gtts:
|
with patch("homeassistant.components.google_translate.tts.gTTS") as mock_gtts:
|
||||||
yield mock_gtts
|
yield mock_gtts
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.fixture(name="setup")
|
||||||
"""Test service call say."""
|
async def setup_fixture(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: dict[str, Any],
|
||||||
|
request: pytest.FixtureRequest,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the test environment."""
|
||||||
|
if request.param == "mock_setup":
|
||||||
|
await mock_setup(hass, config)
|
||||||
|
elif request.param == "mock_config_entry_setup":
|
||||||
|
await mock_config_entry_setup(hass, config)
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Invalid setup fixture")
|
||||||
|
|
||||||
await async_setup_component(
|
|
||||||
hass, tts.DOMAIN, {tts.DOMAIN: {"platform": "google_translate"}}
|
@pytest.fixture(name="config")
|
||||||
|
def config_fixture() -> dict[str, Any]:
|
||||||
|
"""Return config."""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
async def mock_setup(hass: HomeAssistant, config: dict[str, Any]) -> None:
|
||||||
|
"""Mock setup."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, tts.DOMAIN, {tts.DOMAIN: {CONF_PLATFORM: DOMAIN} | config}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def mock_config_entry_setup(hass: HomeAssistant, config: dict[str, Any]) -> None:
|
||||||
|
"""Mock config entry setup."""
|
||||||
|
default_config = {tts.CONF_LANG: "en", CONF_TLD: "com"}
|
||||||
|
config_entry = MockConfigEntry(domain=DOMAIN, data=default_config | config)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_tts_service(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Test tts service."""
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,22 +153,43 @@ async def test_service_say(hass: HomeAssistant, mock_gtts, calls) -> None:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say_german_config(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.mark.parametrize("config", [{tts.CONF_LANG: "de"}])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_de_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_service_say_german_config(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test service call say with german code in the config."""
|
"""Test service call say with german code in the config."""
|
||||||
|
|
||||||
await async_setup_component(
|
|
||||||
hass,
|
|
||||||
tts.DOMAIN,
|
|
||||||
{tts.DOMAIN: {"platform": "google_translate", "language": "de"}},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,25 +203,44 @@ async def test_service_say_german_config(hass: HomeAssistant, mock_gtts, calls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_LANGUAGE: "de",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_LANGUAGE: "de",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
async def test_service_say_german_service(
|
async def test_service_say_german_service(
|
||||||
hass: HomeAssistant, mock_gtts, calls
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test service call say with german code in the service."""
|
"""Test service call say with german code in the service."""
|
||||||
|
|
||||||
config = {
|
|
||||||
tts.DOMAIN: {"platform": "google_translate", "service_name": "google_say"}
|
|
||||||
}
|
|
||||||
|
|
||||||
await async_setup_component(hass, tts.DOMAIN, config)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
tts.ATTR_LANGUAGE: "de",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -149,22 +254,43 @@ async def test_service_say_german_service(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say_en_uk_config(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.mark.parametrize("config", [{tts.CONF_LANG: "en-uk"}])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_co_uk",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_service_say_en_uk_config(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test service call say with en-uk code in the config."""
|
"""Test service call say with en-uk code in the config."""
|
||||||
|
|
||||||
await async_setup_component(
|
|
||||||
hass,
|
|
||||||
tts.DOMAIN,
|
|
||||||
{tts.DOMAIN: {"platform": "google_translate", "language": "en-uk"}},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -178,23 +304,44 @@ async def test_service_say_en_uk_config(hass: HomeAssistant, mock_gtts, calls) -
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say_en_uk_service(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_LANGUAGE: "en-uk",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_LANGUAGE: "en-uk",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_service_say_en_uk_service(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test service call say with en-uk code in the config."""
|
"""Test service call say with en-uk code in the config."""
|
||||||
|
|
||||||
await async_setup_component(
|
|
||||||
hass,
|
|
||||||
tts.DOMAIN,
|
|
||||||
{tts.DOMAIN: {"platform": "google_translate"}},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
tts.ATTR_LANGUAGE: "en-uk",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -208,21 +355,44 @@ async def test_service_say_en_uk_service(hass: HomeAssistant, mock_gtts, calls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say_en_couk(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_OPTIONS: {"tld": "co.uk"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
tts.ATTR_OPTIONS: {"tld": "co.uk"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_service_say_en_couk(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test service call say in co.uk tld accent."""
|
"""Test service call say in co.uk tld accent."""
|
||||||
|
|
||||||
await async_setup_component(
|
|
||||||
hass, tts.DOMAIN, {tts.DOMAIN: {"platform": "google_translate"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
tts.ATTR_OPTIONS: {"tld": "co.uk"},
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -238,20 +408,44 @@ async def test_service_say_en_couk(hass: HomeAssistant, mock_gtts, calls) -> Non
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_service_say_error(hass: HomeAssistant, mock_gtts, calls) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
("setup", "tts_service", "service_data"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"mock_setup",
|
||||||
|
"google_translate_say",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"mock_config_entry_setup",
|
||||||
|
"speak",
|
||||||
|
{
|
||||||
|
ATTR_ENTITY_ID: "tts.google_en_com",
|
||||||
|
tts.ATTR_MEDIA_PLAYER_ENTITY_ID: "media_player.something",
|
||||||
|
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
indirect=["setup"],
|
||||||
|
)
|
||||||
|
async def test_service_say_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_gtts: MagicMock,
|
||||||
|
calls: list[ServiceCall],
|
||||||
|
setup: str,
|
||||||
|
tts_service: str,
|
||||||
|
service_data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test service call say with http response 400."""
|
"""Test service call say with http response 400."""
|
||||||
mock_gtts.return_value.write_to_fp.side_effect = gTTSError
|
mock_gtts.return_value.write_to_fp.side_effect = gTTSError
|
||||||
await async_setup_component(
|
|
||||||
hass, tts.DOMAIN, {tts.DOMAIN: {"platform": "google_translate"}}
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
tts.DOMAIN,
|
tts.DOMAIN,
|
||||||
"google_translate_say",
|
tts_service,
|
||||||
{
|
service_data,
|
||||||
"entity_id": "media_player.something",
|
|
||||||
tts.ATTR_MESSAGE: "There is a person at the front door.",
|
|
||||||
},
|
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue