diff --git a/homeassistant/components/icloud/.translations/en.json b/homeassistant/components/icloud/.translations/en.json index 73ca1b31256..19a07a19c68 100644 --- a/homeassistant/components/icloud/.translations/en.json +++ b/homeassistant/components/icloud/.translations/en.json @@ -20,7 +20,8 @@ "user": { "data": { "password": "Password", - "username": "Email" + "username": "Email", + "with_family": "With family" }, "description": "Enter your credentials", "title": "iCloud credentials" diff --git a/homeassistant/components/icloud/__init__.py b/homeassistant/components/icloud/__init__.py index 1131a4eecc9..ba0f42432cc 100644 --- a/homeassistant/components/icloud/__init__.py +++ b/homeassistant/components/icloud/__init__.py @@ -14,8 +14,10 @@ from .account import IcloudAccount from .const import ( CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, + CONF_WITH_FAMILY, DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_MAX_INTERVAL, + DEFAULT_WITH_FAMILY, DOMAIN, PLATFORMS, STORAGE_KEY, @@ -71,6 +73,7 @@ ACCOUNT_SCHEMA = vol.Schema( { vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_WITH_FAMILY, default=DEFAULT_WITH_FAMILY): cv.boolean, vol.Optional(CONF_MAX_INTERVAL, default=DEFAULT_MAX_INTERVAL): cv.positive_int, vol.Optional( CONF_GPS_ACCURACY_THRESHOLD, default=DEFAULT_GPS_ACCURACY_THRESHOLD @@ -110,6 +113,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool username = entry.data[CONF_USERNAME] password = entry.data[CONF_PASSWORD] + with_family = entry.data[CONF_WITH_FAMILY] max_interval = entry.data[CONF_MAX_INTERVAL] gps_accuracy_threshold = entry.data[CONF_GPS_ACCURACY_THRESHOLD] @@ -120,7 +124,13 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool icloud_dir = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) account = IcloudAccount( - hass, username, password, icloud_dir, max_interval, gps_accuracy_threshold, + hass, + username, + password, + icloud_dir, + with_family, + max_interval, + gps_accuracy_threshold, ) await hass.async_add_executor_job(account.setup) diff --git a/homeassistant/components/icloud/account.py b/homeassistant/components/icloud/account.py index bb3742174d7..6c4d9c5c25f 100644 --- a/homeassistant/components/icloud/account.py +++ b/homeassistant/components/icloud/account.py @@ -78,6 +78,7 @@ class IcloudAccount: username: str, password: str, icloud_dir: Store, + with_family: bool, max_interval: int, gps_accuracy_threshold: int, ): @@ -85,6 +86,7 @@ class IcloudAccount: self.hass = hass self._username = username self._password = password + self._with_family = with_family self._fetch_interval = max_interval self._max_interval = max_interval self._gps_accuracy_threshold = gps_accuracy_threshold @@ -102,7 +104,10 @@ class IcloudAccount: """Set up an iCloud account.""" try: self.api = PyiCloudService( - self._username, self._password, self._icloud_dir.path + self._username, + self._password, + self._icloud_dir.path, + with_family=self._with_family, ) except PyiCloudFailedLoginException as error: self.api = None diff --git a/homeassistant/components/icloud/config_flow.py b/homeassistant/components/icloud/config_flow.py index 72ff6e6481d..052e5b98379 100644 --- a/homeassistant/components/icloud/config_flow.py +++ b/homeassistant/components/icloud/config_flow.py @@ -17,8 +17,10 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from .const import ( CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, + CONF_WITH_FAMILY, DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_MAX_INTERVAL, + DEFAULT_WITH_FAMILY, STORAGE_KEY, STORAGE_VERSION, ) @@ -41,7 +43,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self.api = None self._username = None self._password = None - self._account_name = None + self._with_family = None self._max_interval = None self._gps_accuracy_threshold = None @@ -64,6 +66,10 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): vol.Required( CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "") ): str, + vol.Optional( + CONF_WITH_FAMILY, + default=user_input.get(CONF_WITH_FAMILY, DEFAULT_WITH_FAMILY), + ): bool, } ), errors=errors or {}, @@ -83,6 +89,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._username = user_input[CONF_USERNAME] self._password = user_input[CONF_PASSWORD] + self._with_family = user_input.get(CONF_WITH_FAMILY, DEFAULT_WITH_FAMILY) 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 @@ -95,7 +102,13 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): try: self.api = await self.hass.async_add_executor_job( - PyiCloudService, self._username, self._password, icloud_dir.path + PyiCloudService, + self._username, + self._password, + icloud_dir.path, + True, + None, + self._with_family, ) except PyiCloudFailedLoginException as error: _LOGGER.error("Error logging into iCloud service: %s", error) @@ -122,6 +135,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): data={ CONF_USERNAME: self._username, CONF_PASSWORD: self._password, + CONF_WITH_FAMILY: self._with_family, CONF_MAX_INTERVAL: self._max_interval, CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, }, @@ -211,6 +225,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): { CONF_USERNAME: self._username, CONF_PASSWORD: self._password, + CONF_WITH_FAMILY: self._with_family, 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 14bd4e498bd..d62bacf1212 100644 --- a/homeassistant/components/icloud/const.py +++ b/homeassistant/components/icloud/const.py @@ -2,9 +2,11 @@ DOMAIN = "icloud" +CONF_WITH_FAMILY = "with_family" CONF_MAX_INTERVAL = "max_interval" CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold" +DEFAULT_WITH_FAMILY = False DEFAULT_MAX_INTERVAL = 30 # min DEFAULT_GPS_ACCURACY_THRESHOLD = 500 # meters diff --git a/homeassistant/components/icloud/strings.json b/homeassistant/components/icloud/strings.json index f1931f7cb5c..6cea7dc1175 100644 --- a/homeassistant/components/icloud/strings.json +++ b/homeassistant/components/icloud/strings.json @@ -7,7 +7,8 @@ "description": "Enter your credentials", "data": { "username": "Email", - "password": "Password" + "password": "Password", + "with_family": "With family" } }, "trusted_device": { diff --git a/tests/components/icloud/test_config_flow.py b/tests/components/icloud/test_config_flow.py index 646d62a09b8..4bce35d0a63 100644 --- a/tests/components/icloud/test_config_flow.py +++ b/tests/components/icloud/test_config_flow.py @@ -12,8 +12,10 @@ from homeassistant.components.icloud.config_flow import ( from homeassistant.components.icloud.const import ( CONF_GPS_ACCURACY_THRESHOLD, CONF_MAX_INTERVAL, + CONF_WITH_FAMILY, DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_MAX_INTERVAL, + DEFAULT_WITH_FAMILY, DOMAIN, ) from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER @@ -25,6 +27,7 @@ from tests.common import MockConfigEntry USERNAME = "username@me.com" USERNAME_2 = "second_username@icloud.com" PASSWORD = "password" +WITH_FAMILY = True MAX_INTERVAL = 15 GPS_ACCURACY_THRESHOLD = 250 @@ -106,7 +109,7 @@ async def test_user(hass: HomeAssistantType, service: MagicMock): assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" - # test with all provided + # test with required result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, @@ -124,20 +127,25 @@ async def test_user_with_cookie( result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, - data={CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD}, + data={ + CONF_USERNAME: USERNAME, + CONF_PASSWORD: PASSWORD, + CONF_WITH_FAMILY: WITH_FAMILY, + }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["result"].unique_id == USERNAME assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_WITH_FAMILY] == WITH_FAMILY 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.""" - # import with username and password + # import with required result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, @@ -153,6 +161,7 @@ async def test_import(hass: HomeAssistantType, service: MagicMock): data={ CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD, + CONF_WITH_FAMILY: WITH_FAMILY, CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, }, @@ -165,7 +174,7 @@ async def test_import_with_cookie( hass: HomeAssistantType, service_authenticated: MagicMock ): """Test import step with presence of a cookie.""" - # import with username and password + # import with required result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, @@ -176,6 +185,7 @@ async def test_import_with_cookie( assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_WITH_FAMILY] == DEFAULT_WITH_FAMILY assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD @@ -186,6 +196,7 @@ async def test_import_with_cookie( data={ CONF_USERNAME: USERNAME_2, CONF_PASSWORD: PASSWORD, + CONF_WITH_FAMILY: WITH_FAMILY, CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, }, @@ -195,6 +206,7 @@ async def test_import_with_cookie( assert result["title"] == USERNAME_2 assert result["data"][CONF_USERNAME] == USERNAME_2 assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_WITH_FAMILY] == WITH_FAMILY assert result["data"][CONF_MAX_INTERVAL] == MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == GPS_ACCURACY_THRESHOLD @@ -209,7 +221,7 @@ async def test_two_accounts_setup( unique_id=USERNAME, ).add_to_hass(hass) - # import with username and password + # import with required result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, @@ -220,6 +232,7 @@ async def test_two_accounts_setup( assert result["title"] == USERNAME_2 assert result["data"][CONF_USERNAME] == USERNAME_2 assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_WITH_FAMILY] == DEFAULT_WITH_FAMILY assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD @@ -361,6 +374,7 @@ async def test_verification_code_success(hass: HomeAssistantType, service: Magic assert result["title"] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_PASSWORD] == PASSWORD + assert result["data"][CONF_WITH_FAMILY] == DEFAULT_WITH_FAMILY assert result["data"][CONF_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD