From 82f94de0b803cc262f624a0f9758008a905a2552 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Thu, 21 Aug 2025 17:10:27 +0200 Subject: [PATCH] Enable country site autodetection in Alexa Devices (#150989) --- .../components/alexa_devices/__init__.py | 30 ++++++++++++++-- .../components/alexa_devices/config_flow.py | 13 +++---- .../components/alexa_devices/const.py | 19 ++++++++++ .../components/alexa_devices/coordinator.py | 3 +- .../components/alexa_devices/manifest.json | 2 +- .../components/alexa_devices/strings.json | 4 --- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/alexa_devices/conftest.py | 5 ++- .../snapshots/test_diagnostics.ambr | 1 - .../alexa_devices/test_config_flow.py | 11 ++---- tests/components/alexa_devices/test_init.py | 35 +++++++++++++++++-- 12 files changed, 92 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/alexa_devices/__init__.py b/homeassistant/components/alexa_devices/__init__.py index 9df0e60850e..c08e2f1c010 100644 --- a/homeassistant/components/alexa_devices/__init__.py +++ b/homeassistant/components/alexa_devices/__init__.py @@ -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) diff --git a/homeassistant/components/alexa_devices/config_flow.py b/homeassistant/components/alexa_devices/config_flow.py index 3e705d73ade..ca00d3e8250 100644 --- a/homeassistant/components/alexa_devices/config_flow.py +++ b/homeassistant/components/alexa_devices/config_flow.py @@ -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, diff --git a/homeassistant/components/alexa_devices/const.py b/homeassistant/components/alexa_devices/const.py index ca0290a10bc..3ade3ad3ecd 100644 --- a/homeassistant/components/alexa_devices/const.py +++ b/homeassistant/components/alexa_devices/const.py @@ -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"}, +} diff --git a/homeassistant/components/alexa_devices/coordinator.py b/homeassistant/components/alexa_devices/coordinator.py index f4a1faa4f81..ac033a487ee 100644 --- a/homeassistant/components/alexa_devices/coordinator.py +++ b/homeassistant/components/alexa_devices/coordinator.py @@ -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], diff --git a/homeassistant/components/alexa_devices/manifest.json b/homeassistant/components/alexa_devices/manifest.json index 90410412dfa..cba3af83f44 100644 --- a/homeassistant/components/alexa_devices/manifest.json +++ b/homeassistant/components/alexa_devices/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["aioamazondevices"], "quality_scale": "silver", - "requirements": ["aioamazondevices==4.0.0"] + "requirements": ["aioamazondevices==5.0.0"] } diff --git a/homeassistant/components/alexa_devices/strings.json b/homeassistant/components/alexa_devices/strings.json index 1b1150d5649..720b357d275 100644 --- a/homeassistant/components/alexa_devices/strings.json +++ b/homeassistant/components/alexa_devices/strings.json @@ -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%]" } }, diff --git a/requirements_all.txt b/requirements_all.txt index 1c6a80b1b73..e89fe196fc6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8b3fc149d14..cd45aacc4b1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/alexa_devices/conftest.py b/tests/components/alexa_devices/conftest.py index 22596706862..3c68b7b7626 100644 --- a/tests/components/alexa_devices/conftest.py +++ b/tests/components/alexa_devices/conftest.py @@ -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"}, diff --git a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr index 95798fca817..0f3c3647e90 100644 --- a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr +++ b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr @@ -47,7 +47,6 @@ }), 'entry': dict({ 'data': dict({ - 'country': 'IT', 'login_data': dict({ 'session': 'test-session', }), diff --git a/tests/components/alexa_devices/test_config_flow.py b/tests/components/alexa_devices/test_config_flow.py index e1b2974184b..e4b0f8aa087 100644 --- a/tests/components/alexa_devices/test_config_flow.py +++ b/tests/components/alexa_devices/test_config_flow.py @@ -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, diff --git a/tests/components/alexa_devices/test_init.py b/tests/components/alexa_devices/test_init.py index 3100cfe5fa9..c628a5e00e7 100644 --- a/tests/components/alexa_devices/test_init.py +++ b/tests/components/alexa_devices/test_init.py @@ -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}"