Google Maps location sharing

Edit: Never mind. I finally looked at the device tracker code and realized the filename for the cookies has changed. Works using .google_maps_location_sharing.cookies.

@michaelarnauts thank you for this component.
Just one suggestion, it would be much safer to remove a full name from the attributes and show a nickname only.
Also, I just noticed that nickname is incorrect, it shows the first name of a person instead of nickname in Google profile. It would be good to fix it if possible.

In case somebody wants to test the accuracy, this should do the trick. Havenā€™t tested it. You will need a locationsharinglib version that exposes the accuracy. 1.1.0 doesnā€™t do this afaik.

Let us know if this is working fine.

See Google Maps location sharing

I have pushed v.1.2 that exposes the accuracy. I will start testing this straight away. I also implemented a small helper for people with 2FA in their google accounts after some contribution by Jeremy Wiebe and i am waiting for feedback from him to standardize on the main line.

What would be a good accuracy value to start with you recon?

Not sure, but personally, I suppose everything with an accuracy above 200 meters could be skippedā€¦ This probably depends on the preference of the user. I also use a device tracker based from my router, so the google maps location is only used for when we are not home. If you only use this component, you might want to use a higer value.

Cool, thanks! version 1.2.1 exposes the accuracy and does not log not getting an account as an exception so the logs of HA are not polluted with junk.

Okay, I will try with 1.2.1 and with gps filtering.

Iā€™m not sure why but I started getting errors in the get_authenticated_person function.

Note that Iā€™m using a different account that is tracking my other accounts but that has no location data for itself. I guess get_authenticated_person() wasnā€™t tested for this case.

home-assistant_1  | Traceback (most recent call last):
home-assistant_1  |   File "/config/deps/lib/python3.6/site-packages/locationsharinglib/locationsharinglib.py", line 305, in get_authenticated_person
home-assistant_1  |     output[9][1],
home-assistant_1  | TypeError: 'NoneType' object is not subscriptable

I suppose this is just debugging, but it is spamming my logs while it shouldnā€™t do that. Isnā€™t there a way to check if there is location data for the account owner before doing this?

1.2.1 made the logging from an exception into a debug message. This should be handled fine. Are you sure you are on 1.2.1 and not 1.2.0?

Ow, right, Iā€™m still on 1.2.0 since 1.2.1 wasnā€™t available on pypi a few moments ago.

Looking at the code, I do notice that we are calling _get_data twice now. Once for the shared users, and once for the authenticated user. The second call will never return data for me, so that call is useless.

True, but we needed a way to handle situations that people actually want to retrieve their accounts which are shared. You can follow that discussion on github on the component.

Donā€™t forget that the call to get_data is cached for 30 seconds so it does not actually happen, just the cache is being retrieved the second time.

Okay, I didnā€™t know about that cache.

Updated component:

"""
Support for Google Maps location sharing.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/device_tracker.google_maps/
"""
import logging
from datetime import timedelta

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
    PLATFORM_SCHEMA, SOURCE_TYPE_GPS)
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
from homeassistant.helpers.event import track_time_interval
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import slugify

_LOGGER = logging.getLogger(__name__)

REQUIREMENTS = ['locationsharinglib==1.2.1']

CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy'
CREDENTIALS_FILE = '.google_maps_location_sharing.cookies'

MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_USERNAME): cv.string,
    vol.Required(CONF_PASSWORD): cv.string,
    vol.Optional(CONF_MAX_GPS_ACCURACY): vol.Coerce(float),
})


def setup_scanner(hass, config: ConfigType, see, discovery_info=None):
    """Set up the scanner."""
    scanner = GoogleMapsScanner(hass, config, see)
    return scanner.success_init


class GoogleMapsScanner(object):
    """Representation of an Google Maps location sharing account."""

    def __init__(self, hass, config: ConfigType, see) -> None:
        """Initialize the scanner."""
        from locationsharinglib import Service
        from locationsharinglib.locationsharinglibexceptions import InvalidUser

        self.see = see
        self.username = config[CONF_USERNAME]
        self.password = config[CONF_PASSWORD]
        self.max_gps_accuracy = config[CONF_MAX_GPS_ACCURACY]

        try:
            self.service = Service(self.username, self.password,
                                   hass.config.path(CREDENTIALS_FILE))
            self._update_info()

            track_time_interval(
                hass, self._update_info, MIN_TIME_BETWEEN_SCANS)

            self.success_init = True

        except InvalidUser:
            _LOGGER.error('You have specified invalid login credentials')
            self.success_init = False

    def _update_info(self, now=None):
        for person in self.service.get_all_people():
            dev_id = 'google_maps_{0}'.format(slugify(person.id))

            if self.max_gps_accuracy is not None and \
                    person.accuracy > self.max_gps_accuracy:
                _LOGGER.info("Ignoring update because expected GPS "
                             "accuracy %s is not met: %s",
                             self.max_gps_accuracy, person.accuracy)
                continue

            attrs = {
                'id': person.id,
                'nickname': person.nickname,
                'full_name': person.full_name,
                'last_seen': person.datetime,
                'address': person.address
            }
            self.see(
                dev_id=dev_id,
                gps=(person.latitude, person.longitude),
                gps_accuracy=person.accuracy,
                picture=person.picture_url,
                source_type=SOURCE_TYPE_GPS,
                attributes=attrs
            )

Lovely! I am on this component for a few hours now, I will let you know how it goes with false positives. I have the accuracy to 150.

No matter how many times i read this thread i cant figure out if im suppose to be able to install locationsharinglib on my hass.io installation on my pi3?

I have added the /config/custom_components/device_tracker/google_maps.py file,
but when i try tu run
core-ssh:~# pip install locationsharinglib

i only get an error:
-bash: bash:: command not found

After updating the component, I get this error at HASS startup:

2018-04-03 21:55:29 ERROR (MainThread) [homeassistant.components.device_tracker] Error setting up platform google_maps
Traceback (most recent call last):
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/components/device_tracker/__init__.py", line 169, in async_setup_platform
    disc_info)
  File "/usr/lib/python3.5/asyncio/futures.py", line 361, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/lib/python3.5/asyncio/tasks.py", line 296, in _wakeup
    future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/lib/python3.5/concurrent/futures/thread.py", line 55, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/var/lib/home-assistant/custom_components/device_tracker/google_maps.py", line 38, in setup_scanner
    scanner = GoogleMapsScanner(hass, config, see)
  File "/var/lib/home-assistant/custom_components/device_tracker/google_maps.py", line 58, in __init__
    self._update_info()
  File "/var/lib/home-assistant/custom_components/device_tracker/google_maps.py", line 71, in _update_info
    dev_id = 'google_maps_{0}'.format(slugify(person.id))
  File "/srv/homeassistant/lib/python3.5/site-packages/homeassistant/util/__init__.py", line 42, in slugify
    text = normalize('NFKD', text)
TypeError: normalize() argument 2 must be str, not None

After this, no more errors, but only 2 of 3 shared devices are discovered. The third is never seen.
Aparently, it cannot get the device id.

With previous version it worked.

I was using a max_gps_accuracy value of 55. I increased it to 150 and now all 3 devices are detected correctly, although the error keeps dumping at startup.

Aparently, now it updates OK. Iā€™ll keep up testingā€¦

After 10 hours, the location does not update. Neither of the 3 devices we have.

FYI, this doesnā€™t appear to work if the ā€œtrackingā€ account has a custom domain, ie if your Gmail account does not end with @gmail.com