Fetch iCloud family devices only when wanted (#32644)

* Fetch iCloud family devices only when wanted

* Review: form data_schema as init schema
This commit is contained in:
Quentame 2020-03-11 20:43:37 +01:00 committed by GitHub
parent cea5cac6e2
commit 01dc81d8fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 59 additions and 11 deletions

View file

@ -20,7 +20,8 @@
"user": { "user": {
"data": { "data": {
"password": "Password", "password": "Password",
"username": "Email" "username": "Email",
"with_family": "With family"
}, },
"description": "Enter your credentials", "description": "Enter your credentials",
"title": "iCloud credentials" "title": "iCloud credentials"

View file

@ -14,8 +14,10 @@ from .account import IcloudAccount
from .const import ( from .const import (
CONF_GPS_ACCURACY_THRESHOLD, CONF_GPS_ACCURACY_THRESHOLD,
CONF_MAX_INTERVAL, CONF_MAX_INTERVAL,
CONF_WITH_FAMILY,
DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD,
DEFAULT_MAX_INTERVAL, DEFAULT_MAX_INTERVAL,
DEFAULT_WITH_FAMILY,
DOMAIN, DOMAIN,
PLATFORMS, PLATFORMS,
STORAGE_KEY, STORAGE_KEY,
@ -71,6 +73,7 @@ ACCOUNT_SCHEMA = vol.Schema(
{ {
vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): 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_MAX_INTERVAL, default=DEFAULT_MAX_INTERVAL): cv.positive_int,
vol.Optional( vol.Optional(
CONF_GPS_ACCURACY_THRESHOLD, default=DEFAULT_GPS_ACCURACY_THRESHOLD 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] username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD] password = entry.data[CONF_PASSWORD]
with_family = entry.data[CONF_WITH_FAMILY]
max_interval = entry.data[CONF_MAX_INTERVAL] max_interval = entry.data[CONF_MAX_INTERVAL]
gps_accuracy_threshold = entry.data[CONF_GPS_ACCURACY_THRESHOLD] 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) icloud_dir = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
account = IcloudAccount( 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) await hass.async_add_executor_job(account.setup)

View file

@ -78,6 +78,7 @@ class IcloudAccount:
username: str, username: str,
password: str, password: str,
icloud_dir: Store, icloud_dir: Store,
with_family: bool,
max_interval: int, max_interval: int,
gps_accuracy_threshold: int, gps_accuracy_threshold: int,
): ):
@ -85,6 +86,7 @@ class IcloudAccount:
self.hass = hass self.hass = hass
self._username = username self._username = username
self._password = password self._password = password
self._with_family = with_family
self._fetch_interval = max_interval self._fetch_interval = max_interval
self._max_interval = max_interval self._max_interval = max_interval
self._gps_accuracy_threshold = gps_accuracy_threshold self._gps_accuracy_threshold = gps_accuracy_threshold
@ -102,7 +104,10 @@ class IcloudAccount:
"""Set up an iCloud account.""" """Set up an iCloud account."""
try: try:
self.api = PyiCloudService( 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: except PyiCloudFailedLoginException as error:
self.api = None self.api = None

View file

@ -17,8 +17,10 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from .const import ( from .const import (
CONF_GPS_ACCURACY_THRESHOLD, CONF_GPS_ACCURACY_THRESHOLD,
CONF_MAX_INTERVAL, CONF_MAX_INTERVAL,
CONF_WITH_FAMILY,
DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD,
DEFAULT_MAX_INTERVAL, DEFAULT_MAX_INTERVAL,
DEFAULT_WITH_FAMILY,
STORAGE_KEY, STORAGE_KEY,
STORAGE_VERSION, STORAGE_VERSION,
) )
@ -41,7 +43,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self.api = None self.api = None
self._username = None self._username = None
self._password = None self._password = None
self._account_name = None self._with_family = None
self._max_interval = None self._max_interval = None
self._gps_accuracy_threshold = None self._gps_accuracy_threshold = None
@ -64,6 +66,10 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
vol.Required( vol.Required(
CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "") CONF_PASSWORD, default=user_input.get(CONF_PASSWORD, "")
): str, ): str,
vol.Optional(
CONF_WITH_FAMILY,
default=user_input.get(CONF_WITH_FAMILY, DEFAULT_WITH_FAMILY),
): bool,
} }
), ),
errors=errors or {}, errors=errors or {},
@ -83,6 +89,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
self._username = user_input[CONF_USERNAME] self._username = user_input[CONF_USERNAME]
self._password = user_input[CONF_PASSWORD] 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._max_interval = user_input.get(CONF_MAX_INTERVAL, DEFAULT_MAX_INTERVAL)
self._gps_accuracy_threshold = user_input.get( self._gps_accuracy_threshold = user_input.get(
CONF_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD CONF_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD
@ -95,7 +102,13 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
try: try:
self.api = await self.hass.async_add_executor_job( 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: except PyiCloudFailedLoginException as error:
_LOGGER.error("Error logging into iCloud service: %s", error) _LOGGER.error("Error logging into iCloud service: %s", error)
@ -122,6 +135,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
data={ data={
CONF_USERNAME: self._username, CONF_USERNAME: self._username,
CONF_PASSWORD: self._password, CONF_PASSWORD: self._password,
CONF_WITH_FAMILY: self._with_family,
CONF_MAX_INTERVAL: self._max_interval, CONF_MAX_INTERVAL: self._max_interval,
CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold,
}, },
@ -211,6 +225,7 @@ class IcloudFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
{ {
CONF_USERNAME: self._username, CONF_USERNAME: self._username,
CONF_PASSWORD: self._password, CONF_PASSWORD: self._password,
CONF_WITH_FAMILY: self._with_family,
CONF_MAX_INTERVAL: self._max_interval, CONF_MAX_INTERVAL: self._max_interval,
CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold, CONF_GPS_ACCURACY_THRESHOLD: self._gps_accuracy_threshold,
} }

View file

@ -2,9 +2,11 @@
DOMAIN = "icloud" DOMAIN = "icloud"
CONF_WITH_FAMILY = "with_family"
CONF_MAX_INTERVAL = "max_interval" CONF_MAX_INTERVAL = "max_interval"
CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold" CONF_GPS_ACCURACY_THRESHOLD = "gps_accuracy_threshold"
DEFAULT_WITH_FAMILY = False
DEFAULT_MAX_INTERVAL = 30 # min DEFAULT_MAX_INTERVAL = 30 # min
DEFAULT_GPS_ACCURACY_THRESHOLD = 500 # meters DEFAULT_GPS_ACCURACY_THRESHOLD = 500 # meters

View file

@ -7,7 +7,8 @@
"description": "Enter your credentials", "description": "Enter your credentials",
"data": { "data": {
"username": "Email", "username": "Email",
"password": "Password" "password": "Password",
"with_family": "With family"
} }
}, },
"trusted_device": { "trusted_device": {

View file

@ -12,8 +12,10 @@ from homeassistant.components.icloud.config_flow import (
from homeassistant.components.icloud.const import ( from homeassistant.components.icloud.const import (
CONF_GPS_ACCURACY_THRESHOLD, CONF_GPS_ACCURACY_THRESHOLD,
CONF_MAX_INTERVAL, CONF_MAX_INTERVAL,
CONF_WITH_FAMILY,
DEFAULT_GPS_ACCURACY_THRESHOLD, DEFAULT_GPS_ACCURACY_THRESHOLD,
DEFAULT_MAX_INTERVAL, DEFAULT_MAX_INTERVAL,
DEFAULT_WITH_FAMILY,
DOMAIN, DOMAIN,
) )
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
@ -25,6 +27,7 @@ from tests.common import MockConfigEntry
USERNAME = "username@me.com" USERNAME = "username@me.com"
USERNAME_2 = "second_username@icloud.com" USERNAME_2 = "second_username@icloud.com"
PASSWORD = "password" PASSWORD = "password"
WITH_FAMILY = True
MAX_INTERVAL = 15 MAX_INTERVAL = 15
GPS_ACCURACY_THRESHOLD = 250 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["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "user" assert result["step_id"] == "user"
# test with all provided # test with required
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, context={"source": SOURCE_USER},
@ -124,20 +127,25 @@ async def test_user_with_cookie(
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_USER}, 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["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["result"].unique_id == USERNAME assert result["result"].unique_id == USERNAME
assert result["title"] == USERNAME assert result["title"] == USERNAME
assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME
assert result["data"][CONF_PASSWORD] == PASSWORD 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_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD
async def test_import(hass: HomeAssistantType, service: MagicMock): async def test_import(hass: HomeAssistantType, service: MagicMock):
"""Test import step.""" """Test import step."""
# import with username and password # import with required
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_IMPORT}, context={"source": SOURCE_IMPORT},
@ -153,6 +161,7 @@ async def test_import(hass: HomeAssistantType, service: MagicMock):
data={ data={
CONF_USERNAME: USERNAME_2, CONF_USERNAME: USERNAME_2,
CONF_PASSWORD: PASSWORD, CONF_PASSWORD: PASSWORD,
CONF_WITH_FAMILY: WITH_FAMILY,
CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_MAX_INTERVAL: MAX_INTERVAL,
CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD,
}, },
@ -165,7 +174,7 @@ async def test_import_with_cookie(
hass: HomeAssistantType, service_authenticated: MagicMock hass: HomeAssistantType, service_authenticated: MagicMock
): ):
"""Test import step with presence of a cookie.""" """Test import step with presence of a cookie."""
# import with username and password # import with required
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_IMPORT}, context={"source": SOURCE_IMPORT},
@ -176,6 +185,7 @@ async def test_import_with_cookie(
assert result["title"] == USERNAME assert result["title"] == USERNAME
assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME
assert result["data"][CONF_PASSWORD] == PASSWORD 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_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD
@ -186,6 +196,7 @@ async def test_import_with_cookie(
data={ data={
CONF_USERNAME: USERNAME_2, CONF_USERNAME: USERNAME_2,
CONF_PASSWORD: PASSWORD, CONF_PASSWORD: PASSWORD,
CONF_WITH_FAMILY: WITH_FAMILY,
CONF_MAX_INTERVAL: MAX_INTERVAL, CONF_MAX_INTERVAL: MAX_INTERVAL,
CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD, CONF_GPS_ACCURACY_THRESHOLD: GPS_ACCURACY_THRESHOLD,
}, },
@ -195,6 +206,7 @@ async def test_import_with_cookie(
assert result["title"] == USERNAME_2 assert result["title"] == USERNAME_2
assert result["data"][CONF_USERNAME] == USERNAME_2 assert result["data"][CONF_USERNAME] == USERNAME_2
assert result["data"][CONF_PASSWORD] == PASSWORD 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_MAX_INTERVAL] == MAX_INTERVAL
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == GPS_ACCURACY_THRESHOLD assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == GPS_ACCURACY_THRESHOLD
@ -209,7 +221,7 @@ async def test_two_accounts_setup(
unique_id=USERNAME, unique_id=USERNAME,
).add_to_hass(hass) ).add_to_hass(hass)
# import with username and password # import with required
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": SOURCE_IMPORT}, context={"source": SOURCE_IMPORT},
@ -220,6 +232,7 @@ async def test_two_accounts_setup(
assert result["title"] == USERNAME_2 assert result["title"] == USERNAME_2
assert result["data"][CONF_USERNAME] == USERNAME_2 assert result["data"][CONF_USERNAME] == USERNAME_2
assert result["data"][CONF_PASSWORD] == PASSWORD 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_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD 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["title"] == USERNAME
assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME
assert result["data"][CONF_PASSWORD] == PASSWORD 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_MAX_INTERVAL] == DEFAULT_MAX_INTERVAL
assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD assert result["data"][CONF_GPS_ACCURACY_THRESHOLD] == DEFAULT_GPS_ACCURACY_THRESHOLD