diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py index f36ad607634..d1e00d65e10 100644 --- a/homeassistant/components/icloud/__init__.py +++ b/homeassistant/components/icloud/__init__.py @@ -23,7 +23,6 @@ from homeassistant.util.dt import utcnow from homeassistant.util.location import distance from .const import ( - CONF_ACCOUNT_NAME, CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, DEFAULT_GPS_ACCURACY_THRESHOLD, @@ -100,7 +99,6 @@ ACCOUNT_SCHEMA = vol.Schema( { vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_ACCOUNT_NAME): cv.string, vol.Optional(CONF_MAX_INTERVAL, default=DEFAULT_MAX_INTERVAL): cv.positive_int, vol.Optional( CONF_GPS_ACCURACY_THRESHOLD, default=DEFAULT_GPS_ACCURACY_THRESHOLD @@ -140,20 +138,13 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] - account_name = entry.data.get(CONF_ACCOUNT_NAME) max_interval = entry.data[CONF_MAX_INTERVAL] gps_accuracy_threshold = entry.data[CONF_GPS_ACCURACY_THRESHOLD] icloud_dir = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) account = IcloudAccount( - hass, - username, - password, - icloud_dir, - account_name, - max_interval, - gps_accuracy_threshold, + hass, username, password, icloud_dir, max_interval, gps_accuracy_threshold, ) await hass.async_add_executor_job(account.setup) hass.data[DOMAIN][username] = account @@ -254,7 +245,6 @@ class IcloudAccount: username: str, password: str, icloud_dir: Store, - account_name: str, max_interval: int, gps_accuracy_threshold: int, ): @@ -262,7 +252,6 @@ class IcloudAccount: self.hass = hass self._username = username self._password = password - self._name = account_name or slugify(username.partition("@")[0]) self._fetch_interval = max_interval self._max_interval = max_interval self._gps_accuracy_threshold = gps_accuracy_threshold @@ -434,11 +423,6 @@ class IcloudAccount: raise Exception(f"No device with name {name}") return result - @property - def name(self) -> str: - """Return the account name.""" - return self._name - @property def username(self) -> str: """Return the account username.""" @@ -471,7 +455,6 @@ class IcloudDevice: def __init__(self, account: IcloudAccount, device: AppleDevice, status): """Initialize the iCloud device.""" self._account = account - account_name = account.name self._device = device self._status = status @@ -494,7 +477,6 @@ class IcloudDevice: self._attrs = { ATTR_ATTRIBUTION: ATTRIBUTION, - CONF_ACCOUNT_NAME: account_name, ATTR_ACCOUNT_FETCH_INTERVAL: self._account.fetch_interval, ATTR_DEVICE_NAME: self._device_model, ATTR_DEVICE_STATUS: None, diff --git a/homeassistant/components/icloud/config_flow.py b/homeassistant/components/icloud/config_flow.py index cf05c07e26f..553ec1a28b4 100644 --- a/homeassistant/components/icloud/config_flow.py +++ b/homeassistant/components/icloud/config_flow.py @@ -8,10 +8,8 @@ import voluptuous as vol from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from homeassistant.util import slugify from .const import ( - CONF_ACCOUNT_NAME, CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, DEFAULT_GPS_ACCURACY_THRESHOLD, @@ -45,14 +43,10 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._trusted_device = None self._verification_code = None - def _configuration_exists(self, username: str, account_name: str) -> bool: - """Return True if username or account_name exists in configuration.""" + def _configuration_exists(self, username: str) -> bool: + """Return True if username exists in configuration.""" for entry in self._async_current_entries(): - if ( - entry.data[CONF_USERNAME] == username - or entry.data.get(CONF_ACCOUNT_NAME) == account_name - or slugify(entry.data[CONF_USERNAME].partition("@")[0]) == account_name - ): + if entry.data[CONF_USERNAME] == username: return True return False @@ -91,13 +85,12 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._username = user_input[CONF_USERNAME] self._password = user_input[CONF_PASSWORD] - self._account_name = user_input.get(CONF_ACCOUNT_NAME) self._max_interval = user_input.get(CONF_MAX_INTERVAL, DEFAULT_MAX_INTERVAL) self._gps_accuracy_threshold = user_input.get( CONF_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD ) - if self._configuration_exists(self._username, self._account_name): + if self._configuration_exists(self._username): errors[CONF_USERNAME] = "username_exists" return await self._show_setup_form(user_input, errors) @@ -119,7 +112,6 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data={ CONF_USERNAME: self._username, CONF_PASSWORD: self._password, - CONF_ACCOUNT_NAME: self._account_name, CONF_MAX_INTERVAL: self._max_interval, CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, }, @@ -127,9 +119,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_import(self, user_input): """Import a config entry.""" - if self._configuration_exists( - user_input[CONF_USERNAME], user_input.get(CONF_ACCOUNT_NAME) - ): + if self._configuration_exists(user_input[CONF_USERNAME]): return self.async_abort(reason="username_exists") return await self.async_step_user(user_input) @@ -214,7 +204,6 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): { CONF_USERNAME: self._username, CONF_PASSWORD: self._password, - CONF_ACCOUNT_NAME: self._account_name, CONF_MAX_INTERVAL: self._max_interval, CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, } diff --git a/homeassistant/components/icloud/const.py b/homeassistant/components/icloud/const.py index c2545d911df..57a3f48936c 100644 --- a/homeassistant/components/icloud/const.py +++ b/homeassistant/components/icloud/const.py @@ -3,7 +3,6 @@ DOMAIN = "icloud" SERVICE_UPDATE = f"{DOMAIN}_update" -CONF_ACCOUNT_NAME = "account_name" CONF_MAX_INTERVAL = "max_interval" CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold" diff --git a/tests/components/icloud/test_config_flow.py b/tests/components/icloud/test_config_flow.py index 5555150befc..035266287f0 100644 --- a/tests/components/icloud/test_config_flow.py +++ b/tests/components/icloud/test_config_flow.py @@ -11,22 +11,21 @@ from homeassistant.components.icloud.config_flow import ( CONF_VERIFICATION_CODE, ) from homeassistant.components.icloud.const import ( - CONF_ACCOUNT_NAME, CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_MAX_INTERVAL, DOMAIN, ) +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.helpers.typing import HomeAssistantType from tests.common import MockConfigEntry USERNAME = "username@me.com" +USERNAME_2 = "second_username@icloud.com" PASSWORD = "password" -ACCOUNT_NAME = "Account name 1 2 3" -ACCOUNT_NAME_FROM_USERNAME = None MAX_INTERVAL = 15 GPS_ACCURACY_THRESHOLD = 250 @@ -92,15 +91,17 @@ def init_config_flow(hass: HomeAssistantType): async def test_user(hass: HomeAssistantType, service: MagicMock): """Test user config.""" - flow = init_config_flow(hass) - - result = await flow.async_step_user() + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER}, data=None + ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" # test with all provided - result = await flow.async_step_user( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + 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_FORM assert result["step_id"] == CONF_TRUSTED_DEVICE @@ -110,41 +111,41 @@ async def test_user_with_cookie( hass: HomeAssistantType, service_with_cookie: MagicMock ): """Test user config with presence of a cookie.""" - flow = init_config_flow(hass) - # test with all provided - result = await flow.async_step_user( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + 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_CREATE_ENTRY assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD - assert result["data"][CONF_ACCOUNT_NAME] == ACCOUNT_NAME_FROM_USERNAME assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD async def test_import(hass: HomeAssistantType, service: MagicMock): """Test import step.""" - flow = init_config_flow(hass) - # import with username and password - result = await flow.async_step_import( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "trusted_device" # import with all - result = await flow.async_step_import( - { - CONF_USERNAME: USERNAME, + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={ + CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD, - CONF_ACCOUNT_NAME: ACCOUNT_NAME, CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, - } + }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "trusted_device" @@ -154,67 +155,80 @@ async def test_import_with_cookie( hass: HomeAssistantType, service_with_cookie: MagicMock ): """Test import step with presence of a cookie.""" - flow = init_config_flow(hass) - # import with username and password - result = await flow.async_step_import( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD - assert result["data"][CONF_ACCOUNT_NAME] == ACCOUNT_NAME_FROM_USERNAME assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD # import with all - result = await flow.async_step_import( - { - CONF_USERNAME: USERNAME, + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={ + CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD, - CONF_ACCOUNT_NAME: ACCOUNT_NAME, CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, - } + }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["title"] == USERNAME - assert result["data"][CONF_USERNAME] == USERNAME + assert result["title"] == USERNAME_2 + assert result["data"][CONF_USERNAME] == USERNAME_2 assert result["data"][CONF_PASSWORD] == PASSWORD - assert result["data"][CONF_ACCOUNT_NAME] == ACCOUNT_NAME assert result["data"][CONF_MAX_INTERVAL] == MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == GPS_ACCURACY_THRESHOLD +async def test_two_accounts_setup( + hass: HomeAssistantType, service_with_cookie: MagicMock +): + """Test to setup two accounts.""" + MockConfigEntry( + domain=DOMAIN, data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + ).add_to_hass(hass) + + # import with username and password + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD}, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == USERNAME_2 + assert result["data"][CONF_USERNAME] == USERNAME_2 + assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL + assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD + + async def test_abort_if_already_setup(hass: HomeAssistantType): """Test we abort if the account is already setup.""" - flow = init_config_flow(hass) MockConfigEntry( domain=DOMAIN, data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} ).add_to_hass(hass) # Should fail, same USERNAME (import) - result = await flow.async_step_import( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT - assert result["reason"] == "username_exists" - - # Should fail, same ACCOUNT_NAME (import) - result = await flow.async_step_import( - { - CONF_USERNAME: "other_username@icloud.com", - CONF_PASSWORD: PASSWORD, - CONF_ACCOUNT_NAME: ACCOUNT_NAME_FROM_USERNAME, - } + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}, ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "username_exists" # Should fail, same USERNAME (flow) - result = await flow.async_step_user( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + 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_FORM assert result["errors"] == {CONF_USERNAME: "username_exists"} @@ -222,14 +236,14 @@ async def test_abort_if_already_setup(hass: HomeAssistantType): async def test_login_failed(hass: HomeAssistantType): """Test when we have errors during login.""" - flow = init_config_flow(hass) - with patch( "pyicloud.base.PyiCloudService.authenticate", side_effect=PyiCloudFailedLoginException(), ): - result = await flow.async_step_user( - {CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD} + 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_FORM assert result["errors"] == {CONF_USERNAME: "login"} @@ -290,7 +304,6 @@ async def test_verification_code_success( assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD - assert result["data"][CONF_ACCOUNT_NAME] is None assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD