Enable country site autodetection in Alexa Devices (#150989)

This commit is contained in:
Simone Chemelli
2025-08-21 17:10:27 +02:00
committed by Paulus Schoutsen
parent 71b2d46afd
commit 82f94de0b8
12 changed files with 92 additions and 35 deletions

View File

@@ -1,11 +1,11 @@
"""Alexa Devices integration."""
from homeassistant.const import Platform
from homeassistant.const import CONF_COUNTRY, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .const import _LOGGER, COUNTRY_DOMAINS, DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .services import async_setup_services
@@ -40,6 +40,32 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo
return True
async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version == 1 and entry.minor_version == 0:
_LOGGER.debug(
"Migrating from version %s.%s", entry.version, entry.minor_version
)
# Convert country in domain
country = entry.data[CONF_COUNTRY]
domain = COUNTRY_DOMAINS.get(country, country)
# Save domain and remove country
new_data = entry.data.copy()
new_data.update({"site": f"https://www.amazon.{domain}"})
hass.config_entries.async_update_entry(
entry, data=new_data, version=1, minor_version=1
)
_LOGGER.info(
"Migration to version %s.%s successful", entry.version, entry.minor_version
)
return True
async def async_unload_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@@ -10,16 +10,14 @@ from aioamazondevices.exceptions import (
CannotAuthenticate,
CannotConnect,
CannotRetrieveData,
WrongCountry,
)
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_CODE, CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import CountrySelector
from .const import CONF_LOGIN_DATA, DOMAIN
@@ -37,7 +35,6 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
session = aiohttp_client.async_create_clientsession(hass)
api = AmazonEchoApi(
session,
data[CONF_COUNTRY],
data[CONF_USERNAME],
data[CONF_PASSWORD],
)
@@ -48,6 +45,9 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str,
class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Alexa Devices."""
VERSION = 1
MINOR_VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
@@ -62,8 +62,6 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
errors["base"] = "invalid_auth"
except CannotRetrieveData:
errors["base"] = "cannot_retrieve_data"
except WrongCountry:
errors["base"] = "wrong_country"
else:
await self.async_set_unique_id(data["customer_info"]["user_id"])
self._abort_if_unique_id_configured()
@@ -78,9 +76,6 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
errors=errors,
data_schema=vol.Schema(
{
vol.Required(
CONF_COUNTRY, default=self.hass.config.country
): CountrySelector(),
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_CODE): cv.string,

View File

@@ -6,3 +6,22 @@ _LOGGER = logging.getLogger(__package__)
DOMAIN = "alexa_devices"
CONF_LOGIN_DATA = "login_data"
DEFAULT_DOMAIN = {"domain": "com"}
COUNTRY_DOMAINS = {
"ar": DEFAULT_DOMAIN,
"at": DEFAULT_DOMAIN,
"au": {"domain": "com.au"},
"be": {"domain": "com.be"},
"br": DEFAULT_DOMAIN,
"gb": {"domain": "co.uk"},
"il": DEFAULT_DOMAIN,
"jp": {"domain": "co.jp"},
"mx": {"domain": "com.mx"},
"no": DEFAULT_DOMAIN,
"nz": {"domain": "com.au"},
"pl": DEFAULT_DOMAIN,
"tr": {"domain": "com.tr"},
"us": DEFAULT_DOMAIN,
"za": {"domain": "co.za"},
}

View File

@@ -11,7 +11,7 @@ from aioamazondevices.exceptions import (
from aiohttp import ClientSession
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@@ -44,7 +44,6 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]):
)
self.api = AmazonEchoApi(
session,
entry.data[CONF_COUNTRY],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
entry.data[CONF_LOGIN_DATA],

View File

@@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "silver",
"requirements": ["aioamazondevices==4.0.0"]
"requirements": ["aioamazondevices==5.0.0"]
}

View File

@@ -1,7 +1,6 @@
{
"common": {
"data_code": "One-time password (OTP code)",
"data_description_country": "The country where your Amazon account is registered.",
"data_description_username": "The email address of your Amazon account.",
"data_description_password": "The password of your Amazon account.",
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported.",
@@ -12,13 +11,11 @@
"step": {
"user": {
"data": {
"country": "[%key:common::config_flow::data::country%]",
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]",
"code": "[%key:component::alexa_devices::common::data_code%]"
},
"data_description": {
"country": "[%key:component::alexa_devices::common::data_description_country%]",
"username": "[%key:component::alexa_devices::common::data_description_username%]",
"password": "[%key:component::alexa_devices::common::data_description_password%]",
"code": "[%key:component::alexa_devices::common::data_description_code%]"
@@ -46,7 +43,6 @@
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"cannot_retrieve_data": "Unable to retrieve data from Amazon. Please try again later.",
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"wrong_country": "Wrong country selected. Please select the country where your Amazon account is registered.",
"unknown": "[%key:common::config_flow::error::unknown%]"
}
},

2
requirements_all.txt generated
View File

@@ -185,7 +185,7 @@ aioairzone-cloud==0.7.1
aioairzone==1.0.0
# homeassistant.components.alexa_devices
aioamazondevices==4.0.0
aioamazondevices==5.0.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station

View File

@@ -173,7 +173,7 @@ aioairzone-cloud==0.7.1
aioairzone==1.0.0
# homeassistant.components.alexa_devices
aioamazondevices==4.0.0
aioamazondevices==5.0.0
# homeassistant.components.ambient_network
# homeassistant.components.ambient_station

View File

@@ -8,9 +8,9 @@ from aioamazondevices.const import DEVICE_TYPE_TO_MODEL
import pytest
from homeassistant.components.alexa_devices.const import CONF_LOGIN_DATA, DOMAIN
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from .const import TEST_COUNTRY, TEST_PASSWORD, TEST_SERIAL_NUMBER, TEST_USERNAME
from .const import TEST_PASSWORD, TEST_SERIAL_NUMBER, TEST_USERNAME
from tests.common import MockConfigEntry
@@ -80,7 +80,6 @@ def mock_config_entry() -> MockConfigEntry:
domain=DOMAIN,
title="Amazon Test Account",
data={
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_LOGIN_DATA: {"session": "test-session"},

View File

@@ -47,7 +47,6 @@
}),
'entry': dict({
'data': dict({
'country': 'IT',
'login_data': dict({
'session': 'test-session',
}),

View File

@@ -6,17 +6,16 @@ from aioamazondevices.exceptions import (
CannotAuthenticate,
CannotConnect,
CannotRetrieveData,
WrongCountry,
)
import pytest
from homeassistant.components.alexa_devices.const import CONF_LOGIN_DATA, DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_CODE, CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from .const import TEST_CODE, TEST_COUNTRY, TEST_PASSWORD, TEST_USERNAME
from .const import TEST_CODE, TEST_PASSWORD, TEST_USERNAME
from tests.common import MockConfigEntry
@@ -37,7 +36,6 @@ async def test_full_flow(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_CODE: TEST_CODE,
@@ -46,7 +44,6 @@ async def test_full_flow(
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == TEST_USERNAME
assert result["data"] == {
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_LOGIN_DATA: {
@@ -63,7 +60,6 @@ async def test_full_flow(
(CannotConnect, "cannot_connect"),
(CannotAuthenticate, "invalid_auth"),
(CannotRetrieveData, "cannot_retrieve_data"),
(WrongCountry, "wrong_country"),
],
)
async def test_flow_errors(
@@ -87,7 +83,6 @@ async def test_flow_errors(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_CODE: TEST_CODE,
@@ -102,7 +97,6 @@ async def test_flow_errors(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_CODE: TEST_CODE,
@@ -131,7 +125,6 @@ async def test_already_configured(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_CODE: TEST_CODE,

View File

@@ -4,12 +4,14 @@ from unittest.mock import AsyncMock
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.alexa_devices.const import DOMAIN
from homeassistant.components.alexa_devices.const import CONF_LOGIN_DATA, DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from . import setup_integration
from .const import TEST_SERIAL_NUMBER
from .const import TEST_COUNTRY, TEST_PASSWORD, TEST_SERIAL_NUMBER, TEST_USERNAME
from tests.common import MockConfigEntry
@@ -28,3 +30,32 @@ async def test_device_info(
)
assert device_entry is not None
assert device_entry == snapshot
async def test_migrate_entry(
hass: HomeAssistant,
mock_amazon_devices_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test successful migration of entry data."""
config_entry = MockConfigEntry(
domain=DOMAIN,
title="Amazon Test Account",
data={
CONF_COUNTRY: TEST_COUNTRY,
CONF_USERNAME: TEST_USERNAME,
CONF_PASSWORD: TEST_PASSWORD,
CONF_LOGIN_DATA: {"session": "test-session"},
},
unique_id=TEST_USERNAME,
version=1,
minor_version=0,
)
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert config_entry.state is ConfigEntryState.LOADED
assert config_entry.minor_version == 1
assert config_entry.data["site"] == f"https://www.amazon.{TEST_COUNTRY}"