From b1384e68002af8b0145fb6d3940b1cb1b2485c5a Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 15 Dec 2025 10:21:37 +0000 Subject: [PATCH 1/5] Add update devices to a job, as it times out on the slow internet connection. Stop reauth flow from trying to create entity again --- homeassistant/components/icloud/account.py | 4 ++-- homeassistant/components/icloud/config_flow.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index d1d35def76bd29..567e35e06187da 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -108,7 +108,7 @@ def setup(self) -> None: if self.api.requires_2fa: # Trigger a new log in to ensure the user enters the 2FA code again. - raise PyiCloudFailedLoginException # noqa: TRY301 + raise PyiCloudFailedLoginException("2FA Required") # noqa: TRY301 except PyiCloudFailedLoginException: self.api = None @@ -151,7 +151,7 @@ def setup(self) -> None: ) self._devices = {} - self.update_devices() + self.hass.add_job(self.update_devices) def update_devices(self) -> None: """Update iCloud devices.""" diff --git a/homeassistant/components/icloud/config_flow.py b/homeassistant/components/icloud/config_flow.py index efcef15b4d038a..a28cf772140147 100644 --- a/homeassistant/components/icloud/config_flow.py +++ b/homeassistant/components/icloud/config_flow.py @@ -155,8 +155,8 @@ async def _validate_and_create_entry(self, user_input, step_id): CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, } - # If this is a password update attempt, update the entry instead of creating one - if step_id == "user": + # If this is a password update attempt, don't try and creating one + if step_id == "user" and not self._existing_entry_data: return self.async_create_entry(title=self._username, data=data) entry = await self.async_set_unique_id(self.unique_id) From 246aef7c6a1aa94965ff1ca84a9d11540cd20ab1 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 16 Dec 2025 09:18:11 +0000 Subject: [PATCH 2/5] Fix iCloud reauthentication flow to correctly handle user source --- homeassistant/components/icloud/config_flow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/icloud/config_flow.py b/homeassistant/components/icloud/config_flow.py index a28cf772140147..f1d7dc474559e0 100644 --- a/homeassistant/components/icloud/config_flow.py +++ b/homeassistant/components/icloud/config_flow.py @@ -16,7 +16,7 @@ ) import voluptuous as vol -from homeassistant.config_entries import ConfigFlow, ConfigFlowResult +from homeassistant.config_entries import SOURCE_USER, ConfigFlow, ConfigFlowResult from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers.storage import Store @@ -156,7 +156,7 @@ async def _validate_and_create_entry(self, user_input, step_id): } # If this is a password update attempt, don't try and creating one - if step_id == "user" and not self._existing_entry_data: + if self.source == SOURCE_USER: return self.async_create_entry(title=self._username, data=data) entry = await self.async_set_unique_id(self.unique_id) From 12bffd032967b2791075a1cf945dcca1d59ba643 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 16 Dec 2025 11:14:36 +0000 Subject: [PATCH 3/5] Add tests for iCloud reauthentication flow and user entry validation --- tests/components/icloud/test_config_flow.py | 48 +++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/components/icloud/test_config_flow.py b/tests/components/icloud/test_config_flow.py index 427fad6380655c..2ce77d121d5e45 100644 --- a/tests/components/icloud/test_config_flow.py +++ b/tests/components/icloud/test_config_flow.py @@ -8,6 +8,7 @@ from homeassistant.components.icloud.config_flow import ( CONF_TRUSTED_DEVICE, CONF_VERIFICATION_CODE, + IcloudFlowHandler, ) from homeassistant.components.icloud.const import ( CONF_GPS_ACCURACY_THRESHOLD, @@ -24,6 +25,7 @@ from homeassistant.data_entry_flow import FlowResultType from .const import ( + DEVICE, MOCK_CONFIG, PASSWORD, PASSWORD_2, @@ -418,3 +420,49 @@ async def test_password_update_wrong_password(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {CONF_PASSWORD: "invalid_auth"} + + +async def test_validate_and_create_entry_user( + hass: HomeAssistant, service_authenticated: MagicMock +) -> None: + """Test _validate_and_create_entry for user flow.""" + + service_authenticated.return_value.devices = DEVICE + + flow = IcloudFlowHandler() + flow.hass = hass + flow.context = {"source": SOURCE_USER} + user_input = {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + result = await flow._validate_and_create_entry(user_input, "user") + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == USERNAME + + +async def test_validate_and_reauth_entry_user( + hass: HomeAssistant, service_authenticated: MagicMock +) -> None: + """Test _validate_and_create_entry for reauth flow.""" + + service_authenticated.return_value.devices = DEVICE + + config_entry = MockConfigEntry( + domain=DOMAIN, data=MOCK_CONFIG, entry_id="test", unique_id=USERNAME + ) + config_entry.add_to_hass(hass) + + flow = IcloudFlowHandler() + flow.hass = hass + flow.context = {"source": "reauth", "unique_id": USERNAME} + flow._existing_entry_data = MOCK_CONFIG + user_input = {CONF_PASSWORD: PASSWORD_2} + + with ( + patch.object(flow, "async_set_unique_id", return_value=config_entry), + patch.object(hass.config_entries, "async_update_entry"), + patch.object(hass.config_entries, "async_reload"), + ): + result = await flow._validate_and_create_entry(user_input, "reauth_confirm") + + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" From 9652f0ddb7b5d48cafef8baf6287f07b34d4daa4 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 17 Dec 2025 09:29:00 +0000 Subject: [PATCH 4/5] Remove IcloudFlowHandler references from iCloud config flow tests --- tests/components/icloud/test_config_flow.py | 48 --------------------- 1 file changed, 48 deletions(-) diff --git a/tests/components/icloud/test_config_flow.py b/tests/components/icloud/test_config_flow.py index 2ce77d121d5e45..427fad6380655c 100644 --- a/tests/components/icloud/test_config_flow.py +++ b/tests/components/icloud/test_config_flow.py @@ -8,7 +8,6 @@ from homeassistant.components.icloud.config_flow import ( CONF_TRUSTED_DEVICE, CONF_VERIFICATION_CODE, - IcloudFlowHandler, ) from homeassistant.components.icloud.const import ( CONF_GPS_ACCURACY_THRESHOLD, @@ -25,7 +24,6 @@ from homeassistant.data_entry_flow import FlowResultType from .const import ( - DEVICE, MOCK_CONFIG, PASSWORD, PASSWORD_2, @@ -420,49 +418,3 @@ async def test_password_update_wrong_password(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {CONF_PASSWORD: "invalid_auth"} - - -async def test_validate_and_create_entry_user( - hass: HomeAssistant, service_authenticated: MagicMock -) -> None: - """Test _validate_and_create_entry for user flow.""" - - service_authenticated.return_value.devices = DEVICE - - flow = IcloudFlowHandler() - flow.hass = hass - flow.context = {"source": SOURCE_USER} - user_input = {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} - result = await flow._validate_and_create_entry(user_input, "user") - - assert result["type"] == FlowResultType.CREATE_ENTRY - assert result["title"] == USERNAME - - -async def test_validate_and_reauth_entry_user( - hass: HomeAssistant, service_authenticated: MagicMock -) -> None: - """Test _validate_and_create_entry for reauth flow.""" - - service_authenticated.return_value.devices = DEVICE - - config_entry = MockConfigEntry( - domain=DOMAIN, data=MOCK_CONFIG, entry_id="test", unique_id=USERNAME - ) - config_entry.add_to_hass(hass) - - flow = IcloudFlowHandler() - flow.hass = hass - flow.context = {"source": "reauth", "unique_id": USERNAME} - flow._existing_entry_data = MOCK_CONFIG - user_input = {CONF_PASSWORD: PASSWORD_2} - - with ( - patch.object(flow, "async_set_unique_id", return_value=config_entry), - patch.object(hass.config_entries, "async_update_entry"), - patch.object(hass.config_entries, "async_reload"), - ): - result = await flow._validate_and_create_entry(user_input, "reauth_confirm") - - assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "reauth_successful" From f0edece85c6039bb3a6788bed8c107be0d054413 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 17 Dec 2025 09:35:56 +0000 Subject: [PATCH 5/5] Remove Extra Changes --- homeassistant/components/icloud/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index 567e35e06187da..d1d35def76bd29 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -108,7 +108,7 @@ def setup(self) -> None: if self.api.requires_2fa: # Trigger a new log in to ensure the user enters the 2FA code again. - raise PyiCloudFailedLoginException("2FA Required") # noqa: TRY301 + raise PyiCloudFailedLoginException # noqa: TRY301 except PyiCloudFailedLoginException: self.api = None @@ -151,7 +151,7 @@ def setup(self) -> None: ) self._devices = {} - self.hass.add_job(self.update_devices) + self.update_devices() def update_devices(self) -> None: """Update iCloud devices."""