diff --git a/homeassistant/components/netatmo/__init__.py b/homeassistant/components/netatmo/__init__.py index 9c92724c543..f11325b02bf 100644 --- a/homeassistant/components/netatmo/__init__.py +++ b/homeassistant/components/netatmo/__init__.py @@ -20,10 +20,11 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_WEBHOOK_ID, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady -from homeassistant.helpers import ( - aiohttp_client, - config_entry_oauth2_flow, - config_validation as cv, +from homeassistant.helpers import aiohttp_client, config_validation as cv +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, + OAuth2Session, + async_get_config_entry_implementation, ) from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.dispatcher import async_dispatcher_send @@ -73,17 +74,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Netatmo from a config entry.""" - implementation = ( - await config_entry_oauth2_flow.async_get_config_entry_implementation( - hass, entry - ) - ) + try: + implementation = await async_get_config_entry_implementation(hass, entry) + except ImplementationUnavailableError as err: + raise ConfigEntryNotReady( + translation_domain=DOMAIN, + translation_key="oauth2_implementation_unavailable", + ) from err # Set unique id if non was set (migration) if not entry.unique_id: hass.config_entries.async_update_entry(entry, unique_id=DOMAIN) - session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation) + session = OAuth2Session(hass, entry, implementation) try: await session.async_ensure_token_valid() except aiohttp.ClientResponseError as ex: diff --git a/homeassistant/components/netatmo/strings.json b/homeassistant/components/netatmo/strings.json index befc9ea843d..0f53cb0dba0 100644 --- a/homeassistant/components/netatmo/strings.json +++ b/homeassistant/components/netatmo/strings.json @@ -143,6 +143,11 @@ } } }, + "exceptions": { + "oauth2_implementation_unavailable": { + "message": "[%key:common::exceptions::oauth2_implementation_unavailable::message%]" + } + }, "options": { "step": { "public_weather": { diff --git a/tests/components/netatmo/common.py b/tests/components/netatmo/common.py index acdc3c491ff..617a0070f5c 100644 --- a/tests/components/netatmo/common.py +++ b/tests/components/netatmo/common.py @@ -119,7 +119,7 @@ def selected_platforms(platforms: list[Platform]) -> Iterator[None]: with ( patch("homeassistant.components.netatmo.data_handler.PLATFORMS", platforms), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_camera.py b/tests/components/netatmo/test_camera.py index 72b18f2e1d2..042806b9324 100644 --- a/tests/components/netatmo/test_camera.py +++ b/tests/components/netatmo/test_camera.py @@ -416,7 +416,7 @@ async def test_camera_reconnect_webhook( ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -515,7 +515,7 @@ async def test_setup_component_no_devices( ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -558,7 +558,7 @@ async def test_camera_image_raises_exception( ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["camera"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_diagnostics.py b/tests/components/netatmo/test_diagnostics.py index 1ada0bdd2bf..dde28fe3bc7 100644 --- a/tests/components/netatmo/test_diagnostics.py +++ b/tests/components/netatmo/test_diagnostics.py @@ -28,7 +28,7 @@ async def test_entry_diagnostics( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", diff --git a/tests/components/netatmo/test_init.py b/tests/components/netatmo/test_init.py index eb052b93288..540927fdb78 100644 --- a/tests/components/netatmo/test_init.py +++ b/tests/components/netatmo/test_init.py @@ -16,6 +16,9 @@ from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_WEBHOOK_ID, Platform from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers.config_entry_oauth2_flow import ( + ImplementationUnavailableError, +) from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util @@ -65,7 +68,7 @@ async def test_setup_component( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, ): @@ -108,7 +111,7 @@ async def test_setup_component_with_config( with ( patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, patch( @@ -181,7 +184,7 @@ async def test_setup_without_https( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth" ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url" @@ -225,7 +228,7 @@ async def test_setup_with_cloud( ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", []), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -295,7 +298,7 @@ async def test_setup_with_cloudhook(hass: HomeAssistant) -> None: ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", []), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url", @@ -340,7 +343,7 @@ async def test_setup_component_with_delay( "pyatmo.AbstractAsyncAuth.async_dropwebhook", side_effect=AsyncMock() ) as mock_dropwebhook, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, patch( @@ -410,7 +413,7 @@ async def test_setup_component_invalid_token_scope(hass: HomeAssistant) -> None: "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, ): @@ -459,12 +462,10 @@ async def test_setup_component_invalid_token( "homeassistant.components.netatmo.api.AsyncConfigEntryNetatmoAuth", ) as mock_auth, patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ) as mock_impl, patch("homeassistant.components.netatmo.webhook_generate_url") as mock_webhook, - patch( - "homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session" - ) as mock_session, + patch("homeassistant.components.netatmo.OAuth2Session") as mock_session, ): mock_auth.return_value.async_post_api_request.side_effect = partial( fake_post_request, hass @@ -557,3 +558,19 @@ async def test_device_remove_devices( ) response = await client.remove_device(dead_device_entry.id, config_entry.entry_id) assert response["success"] + + +async def test_oauth_implementation_not_available( + hass: HomeAssistant, config_entry: MockConfigEntry +) -> None: + """Test that unavailable OAuth implementation raises ConfigEntryNotReady.""" + config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.netatmo.async_get_config_entry_implementation", + side_effect=ImplementationUnavailableError, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert config_entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/netatmo/test_light.py b/tests/components/netatmo/test_light.py index 16a3ac2aaeb..0ede4146044 100644 --- a/tests/components/netatmo/test_light.py +++ b/tests/components/netatmo/test_light.py @@ -129,7 +129,7 @@ async def test_setup_component_no_devices(hass: HomeAssistant, config_entry) -> ) as mock_auth, patch("homeassistant.components.netatmo.data_handler.PLATFORMS", ["light"]), patch( - "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation", + "homeassistant.components.netatmo.async_get_config_entry_implementation", ), patch( "homeassistant.components.netatmo.webhook_generate_url",