Improving icloud device tracker (#14078)
* Improving icloud device tracker * Adding config validations for new values * Adding config validations for new values * Moving icloud specific setup to platform schema. Setting default in platform schema.
This commit is contained in:
parent
10505d542a
commit
9c7523d7b0
1 changed files with 38 additions and 25 deletions
|
@ -24,8 +24,9 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
REQUIREMENTS = ['pyicloud==0.9.1']
|
REQUIREMENTS = ['pyicloud==0.9.1']
|
||||||
|
|
||||||
CONF_IGNORED_DEVICES = 'ignored_devices'
|
|
||||||
CONF_ACCOUNTNAME = 'account_name'
|
CONF_ACCOUNTNAME = 'account_name'
|
||||||
|
CONF_MAX_INTERVAL = 'max_interval'
|
||||||
|
CONF_GPS_ACCURACY_THRESHOLD = 'gps_accuracy_threshold'
|
||||||
|
|
||||||
# entity attributes
|
# entity attributes
|
||||||
ATTR_ACCOUNTNAME = 'account_name'
|
ATTR_ACCOUNTNAME = 'account_name'
|
||||||
|
@ -64,13 +65,15 @@ DEVICESTATUSCODES = {
|
||||||
SERVICE_SCHEMA = vol.Schema({
|
SERVICE_SCHEMA = vol.Schema({
|
||||||
vol.Optional(ATTR_ACCOUNTNAME): vol.All(cv.ensure_list, [cv.slugify]),
|
vol.Optional(ATTR_ACCOUNTNAME): vol.All(cv.ensure_list, [cv.slugify]),
|
||||||
vol.Optional(ATTR_DEVICENAME): cv.slugify,
|
vol.Optional(ATTR_DEVICENAME): cv.slugify,
|
||||||
vol.Optional(ATTR_INTERVAL): cv.positive_int,
|
vol.Optional(ATTR_INTERVAL): cv.positive_int
|
||||||
})
|
})
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
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(ATTR_ACCOUNTNAME): cv.slugify,
|
vol.Optional(ATTR_ACCOUNTNAME): cv.slugify,
|
||||||
|
vol.Optional(CONF_MAX_INTERVAL, default=30): cv.positive_int,
|
||||||
|
vol.Optional(CONF_GPS_ACCURACY_THRESHOLD, default=1000): cv.positive_int
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,8 +82,11 @@ def setup_scanner(hass, config: dict, see, discovery_info=None):
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
account = config.get(CONF_ACCOUNTNAME, slugify(username.partition('@')[0]))
|
account = config.get(CONF_ACCOUNTNAME, slugify(username.partition('@')[0]))
|
||||||
|
max_interval = config.get(CONF_MAX_INTERVAL)
|
||||||
|
gps_accuracy_threshold = config.get(CONF_GPS_ACCURACY_THRESHOLD)
|
||||||
|
|
||||||
icloudaccount = Icloud(hass, username, password, account, see)
|
icloudaccount = Icloud(hass, username, password, account, max_interval,
|
||||||
|
gps_accuracy_threshold, see)
|
||||||
|
|
||||||
if icloudaccount.api is not None:
|
if icloudaccount.api is not None:
|
||||||
ICLOUDTRACKERS[account] = icloudaccount
|
ICLOUDTRACKERS[account] = icloudaccount
|
||||||
|
@ -96,6 +102,7 @@ def setup_scanner(hass, config: dict, see, discovery_info=None):
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
if account in ICLOUDTRACKERS:
|
if account in ICLOUDTRACKERS:
|
||||||
ICLOUDTRACKERS[account].lost_iphone(devicename)
|
ICLOUDTRACKERS[account].lost_iphone(devicename)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, 'icloud_lost_iphone', lost_iphone,
|
hass.services.register(DOMAIN, 'icloud_lost_iphone', lost_iphone,
|
||||||
schema=SERVICE_SCHEMA)
|
schema=SERVICE_SCHEMA)
|
||||||
|
|
||||||
|
@ -106,6 +113,7 @@ def setup_scanner(hass, config: dict, see, discovery_info=None):
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
if account in ICLOUDTRACKERS:
|
if account in ICLOUDTRACKERS:
|
||||||
ICLOUDTRACKERS[account].update_icloud(devicename)
|
ICLOUDTRACKERS[account].update_icloud(devicename)
|
||||||
|
|
||||||
hass.services.register(DOMAIN, 'icloud_update', update_icloud,
|
hass.services.register(DOMAIN, 'icloud_update', update_icloud,
|
||||||
schema=SERVICE_SCHEMA)
|
schema=SERVICE_SCHEMA)
|
||||||
|
|
||||||
|
@ -115,6 +123,7 @@ def setup_scanner(hass, config: dict, see, discovery_info=None):
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
if account in ICLOUDTRACKERS:
|
if account in ICLOUDTRACKERS:
|
||||||
ICLOUDTRACKERS[account].reset_account_icloud()
|
ICLOUDTRACKERS[account].reset_account_icloud()
|
||||||
|
|
||||||
hass.services.register(DOMAIN, 'icloud_reset_account',
|
hass.services.register(DOMAIN, 'icloud_reset_account',
|
||||||
reset_account_icloud, schema=SERVICE_SCHEMA)
|
reset_account_icloud, schema=SERVICE_SCHEMA)
|
||||||
|
|
||||||
|
@ -137,7 +146,8 @@ def setup_scanner(hass, config: dict, see, discovery_info=None):
|
||||||
class Icloud(DeviceScanner):
|
class Icloud(DeviceScanner):
|
||||||
"""Representation of an iCloud account."""
|
"""Representation of an iCloud account."""
|
||||||
|
|
||||||
def __init__(self, hass, username, password, name, see):
|
def __init__(self, hass, username, password, name, max_interval,
|
||||||
|
gps_accuracy_threshold, see):
|
||||||
"""Initialize an iCloud account."""
|
"""Initialize an iCloud account."""
|
||||||
self.hass = hass
|
self.hass = hass
|
||||||
self.username = username
|
self.username = username
|
||||||
|
@ -148,6 +158,8 @@ class Icloud(DeviceScanner):
|
||||||
self.seen_devices = {}
|
self.seen_devices = {}
|
||||||
self._overridestates = {}
|
self._overridestates = {}
|
||||||
self._intervals = {}
|
self._intervals = {}
|
||||||
|
self._max_interval = max_interval
|
||||||
|
self._gps_accuracy_threshold = gps_accuracy_threshold
|
||||||
self.see = see
|
self.see = see
|
||||||
|
|
||||||
self._trusted_device = None
|
self._trusted_device = None
|
||||||
|
@ -348,7 +360,7 @@ class Icloud(DeviceScanner):
|
||||||
self._overridestates[devicename] = None
|
self._overridestates[devicename] = None
|
||||||
|
|
||||||
if currentzone is not None:
|
if currentzone is not None:
|
||||||
self._intervals[devicename] = 30
|
self._intervals[devicename] = self._max_interval
|
||||||
return
|
return
|
||||||
|
|
||||||
if mindistance is None:
|
if mindistance is None:
|
||||||
|
@ -363,7 +375,6 @@ class Icloud(DeviceScanner):
|
||||||
|
|
||||||
if interval > 180:
|
if interval > 180:
|
||||||
# Three hour drive? This is far enough that they might be flying
|
# Three hour drive? This is far enough that they might be flying
|
||||||
# home - check every half hour
|
|
||||||
interval = 30
|
interval = 30
|
||||||
|
|
||||||
if battery is not None and battery <= 33 and mindistance > 3:
|
if battery is not None and battery <= 33 and mindistance > 3:
|
||||||
|
@ -403,22 +414,24 @@ class Icloud(DeviceScanner):
|
||||||
status = device.status(DEVICESTATUSSET)
|
status = device.status(DEVICESTATUSSET)
|
||||||
battery = status.get('batteryLevel', 0) * 100
|
battery = status.get('batteryLevel', 0) * 100
|
||||||
location = status['location']
|
location = status['location']
|
||||||
if location:
|
if location and location['horizontalAccuracy']:
|
||||||
self.determine_interval(
|
horizontal_accuracy = int(location['horizontalAccuracy'])
|
||||||
devicename, location['latitude'],
|
if horizontal_accuracy < self._gps_accuracy_threshold:
|
||||||
location['longitude'], battery)
|
self.determine_interval(
|
||||||
interval = self._intervals.get(devicename, 1)
|
devicename, location['latitude'],
|
||||||
attrs[ATTR_INTERVAL] = interval
|
location['longitude'], battery)
|
||||||
accuracy = location['horizontalAccuracy']
|
interval = self._intervals.get(devicename, 1)
|
||||||
kwargs['dev_id'] = dev_id
|
attrs[ATTR_INTERVAL] = interval
|
||||||
kwargs['host_name'] = status['name']
|
accuracy = location['horizontalAccuracy']
|
||||||
kwargs['gps'] = (location['latitude'],
|
kwargs['dev_id'] = dev_id
|
||||||
location['longitude'])
|
kwargs['host_name'] = status['name']
|
||||||
kwargs['battery'] = battery
|
kwargs['gps'] = (location['latitude'],
|
||||||
kwargs['gps_accuracy'] = accuracy
|
location['longitude'])
|
||||||
kwargs[ATTR_ATTRIBUTES] = attrs
|
kwargs['battery'] = battery
|
||||||
self.see(**kwargs)
|
kwargs['gps_accuracy'] = accuracy
|
||||||
self.seen_devices[devicename] = True
|
kwargs[ATTR_ATTRIBUTES] = attrs
|
||||||
|
self.see(**kwargs)
|
||||||
|
self.seen_devices[devicename] = True
|
||||||
except PyiCloudNoDevicesException:
|
except PyiCloudNoDevicesException:
|
||||||
_LOGGER.error("No iCloud Devices found")
|
_LOGGER.error("No iCloud Devices found")
|
||||||
|
|
||||||
|
@ -434,7 +447,7 @@ class Icloud(DeviceScanner):
|
||||||
device.play_sound()
|
device.play_sound()
|
||||||
|
|
||||||
def update_icloud(self, devicename=None):
|
def update_icloud(self, devicename=None):
|
||||||
"""Authenticate against iCloud and scan for devices."""
|
"""Request device information from iCloud and update device_tracker."""
|
||||||
from pyicloud.exceptions import PyiCloudNoDevicesException
|
from pyicloud.exceptions import PyiCloudNoDevicesException
|
||||||
|
|
||||||
if self.api is None:
|
if self.api is None:
|
||||||
|
@ -443,13 +456,13 @@ class Icloud(DeviceScanner):
|
||||||
try:
|
try:
|
||||||
if devicename is not None:
|
if devicename is not None:
|
||||||
if devicename in self.devices:
|
if devicename in self.devices:
|
||||||
self.devices[devicename].location()
|
self.update_device(devicename)
|
||||||
else:
|
else:
|
||||||
_LOGGER.error("devicename %s unknown for account %s",
|
_LOGGER.error("devicename %s unknown for account %s",
|
||||||
devicename, self._attrs[ATTR_ACCOUNTNAME])
|
devicename, self._attrs[ATTR_ACCOUNTNAME])
|
||||||
else:
|
else:
|
||||||
for device in self.devices:
|
for device in self.devices:
|
||||||
self.devices[device].location()
|
self.update_device(device)
|
||||||
except PyiCloudNoDevicesException:
|
except PyiCloudNoDevicesException:
|
||||||
_LOGGER.error("No iCloud Devices found")
|
_LOGGER.error("No iCloud Devices found")
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue