Add devices check to iCloud config flow (#31950)
* Add devices check to iCloud config flow * Some test rename * Bump pyicloud to catch KeyError
This commit is contained in:
parent
8c52e2c923
commit
2e802c88f8
8 changed files with 64 additions and 15 deletions
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Account already configured"
|
||||
"already_configured": "Account already configured",
|
||||
"no_device": "None of your devices have \"Find my iPhone\" activated"
|
||||
},
|
||||
"error": {
|
||||
"login": "Login error: please check your email & password",
|
||||
|
|
|
@ -5,7 +5,11 @@ import operator
|
|||
from typing import Dict
|
||||
|
||||
from pyicloud import PyiCloudService
|
||||
from pyicloud.exceptions import PyiCloudFailedLoginException, PyiCloudNoDevicesException
|
||||
from pyicloud.exceptions import (
|
||||
PyiCloudFailedLoginException,
|
||||
PyiCloudNoDevicesException,
|
||||
PyiCloudServiceNotActivatedException,
|
||||
)
|
||||
from pyicloud.services.findmyiphone import AppleDevice
|
||||
|
||||
from homeassistant.components.zone import async_active_zone
|
||||
|
@ -109,7 +113,7 @@ class IcloudAccount:
|
|||
api_devices = self.api.devices
|
||||
# Gets device owners infos
|
||||
user_info = api_devices.response["userInfo"]
|
||||
except (KeyError, PyiCloudNoDevicesException):
|
||||
except (PyiCloudServiceNotActivatedException, PyiCloudNoDevicesException):
|
||||
_LOGGER.error("No iCloud device found")
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
|
|
|
@ -3,7 +3,12 @@ import logging
|
|||
import os
|
||||
|
||||
from pyicloud import PyiCloudService
|
||||
from pyicloud.exceptions import PyiCloudException, PyiCloudFailedLoginException
|
||||
from pyicloud.exceptions import (
|
||||
PyiCloudException,
|
||||
PyiCloudFailedLoginException,
|
||||
PyiCloudNoDevicesException,
|
||||
PyiCloudServiceNotActivatedException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -101,6 +106,17 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
if self.api.requires_2sa:
|
||||
return await self.async_step_trusted_device()
|
||||
|
||||
try:
|
||||
devices = await self.hass.async_add_executor_job(
|
||||
getattr, self.api, "devices"
|
||||
)
|
||||
if not devices:
|
||||
raise PyiCloudNoDevicesException()
|
||||
except (PyiCloudServiceNotActivatedException, PyiCloudNoDevicesException):
|
||||
_LOGGER.error("No device found in the iCloud account: %s", self._username)
|
||||
self.api = None
|
||||
return self.async_abort(reason="no_device")
|
||||
|
||||
return self.async_create_entry(
|
||||
title=self._username,
|
||||
data={
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Apple iCloud",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/icloud",
|
||||
"requirements": ["pyicloud==0.9.3"],
|
||||
"requirements": ["pyicloud==0.9.4"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@Quentame"]
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
"validate_verification_code": "Failed to verify your verification code, choose a trust device and start the verification again"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Account already configured"
|
||||
"already_configured": "Account already configured",
|
||||
"no_device": "None of your devices have \"Find my iPhone\" activated"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1312,7 +1312,7 @@ pyhomeworks==0.0.6
|
|||
pyialarm==0.3
|
||||
|
||||
# homeassistant.components.icloud
|
||||
pyicloud==0.9.3
|
||||
pyicloud==0.9.4
|
||||
|
||||
# homeassistant.components.intesishome
|
||||
pyintesishome==1.6
|
||||
|
|
|
@ -480,7 +480,7 @@ pyheos==0.6.0
|
|||
pyhomematic==0.1.65
|
||||
|
||||
# homeassistant.components.icloud
|
||||
pyicloud==0.9.3
|
||||
pyicloud==0.9.4
|
||||
|
||||
# homeassistant.components.ipma
|
||||
pyipma==2.0.5
|
||||
|
|
|
@ -46,8 +46,8 @@ def mock_controller_service():
|
|||
yield service_mock
|
||||
|
||||
|
||||
@pytest.fixture(name="service_with_cookie")
|
||||
def mock_controller_service_with_cookie():
|
||||
@pytest.fixture(name="service_authenticated")
|
||||
def mock_controller_service_authenticated():
|
||||
"""Mock a successful service while already authenticate."""
|
||||
with patch(
|
||||
"homeassistant.components.icloud.config_flow.PyiCloudService"
|
||||
|
@ -59,6 +59,20 @@ def mock_controller_service_with_cookie():
|
|||
yield service_mock
|
||||
|
||||
|
||||
@pytest.fixture(name="service_authenticated_no_device")
|
||||
def mock_controller_service_authenticated_no_device():
|
||||
"""Mock a successful service while already authenticate, but without device."""
|
||||
with patch(
|
||||
"homeassistant.components.icloud.config_flow.PyiCloudService"
|
||||
) as service_mock:
|
||||
service_mock.return_value.requires_2sa = False
|
||||
service_mock.return_value.trusted_devices = TRUSTED_DEVICES
|
||||
service_mock.return_value.send_verification_code = Mock(return_value=True)
|
||||
service_mock.return_value.validate_verification_code = Mock(return_value=True)
|
||||
service_mock.return_value.devices = {}
|
||||
yield service_mock
|
||||
|
||||
|
||||
@pytest.fixture(name="service_send_verification_code_failed")
|
||||
def mock_controller_service_send_verification_code_failed():
|
||||
"""Mock a failed service during sending verification code step."""
|
||||
|
@ -103,7 +117,7 @@ async def test_user(hass: HomeAssistantType, service: MagicMock):
|
|||
|
||||
|
||||
async def test_user_with_cookie(
|
||||
hass: HomeAssistantType, service_with_cookie: MagicMock
|
||||
hass: HomeAssistantType, service_authenticated: MagicMock
|
||||
):
|
||||
"""Test user config with presence of a cookie."""
|
||||
# test with all provided
|
||||
|
@ -148,7 +162,7 @@ async def test_import(hass: HomeAssistantType, service: MagicMock):
|
|||
|
||||
|
||||
async def test_import_with_cookie(
|
||||
hass: HomeAssistantType, service_with_cookie: MagicMock
|
||||
hass: HomeAssistantType, service_authenticated: MagicMock
|
||||
):
|
||||
"""Test import step with presence of a cookie."""
|
||||
# import with username and password
|
||||
|
@ -186,7 +200,7 @@ async def test_import_with_cookie(
|
|||
|
||||
|
||||
async def test_two_accounts_setup(
|
||||
hass: HomeAssistantType, service_with_cookie: MagicMock
|
||||
hass: HomeAssistantType, service_authenticated: MagicMock
|
||||
):
|
||||
"""Test to setup two accounts."""
|
||||
MockConfigEntry(
|
||||
|
@ -210,7 +224,7 @@ async def test_two_accounts_setup(
|
|||
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD
|
||||
|
||||
|
||||
async def test_abort_if_already_setup(hass: HomeAssistantType):
|
||||
async def test_already_setup(hass: HomeAssistantType):
|
||||
"""Test we abort if the account is already setup."""
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -240,7 +254,7 @@ async def test_abort_if_already_setup(hass: HomeAssistantType):
|
|||
async def test_login_failed(hass: HomeAssistantType):
|
||||
"""Test when we have errors during login."""
|
||||
with patch(
|
||||
"pyicloud.base.PyiCloudService.authenticate",
|
||||
"homeassistant.components.icloud.config_flow.PyiCloudService.authenticate",
|
||||
side_effect=PyiCloudFailedLoginException(),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
@ -252,6 +266,19 @@ async def test_login_failed(hass: HomeAssistantType):
|
|||
assert result["errors"] == {CONF_USERNAME: "login"}
|
||||
|
||||
|
||||
async def test_no_device(
|
||||
hass: HomeAssistantType, service_authenticated_no_device: MagicMock
|
||||
):
|
||||
"""Test when we have no devices."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "no_device"
|
||||
|
||||
|
||||
async def test_trusted_device(hass: HomeAssistantType, service: MagicMock):
|
||||
"""Test trusted_device step."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
|
Loading…
Add table
Reference in a new issue