Life360 Device Tracker Platform

This is great! thank you so much. I got this integrated into HA within a few minutes!

I’ve managed to get a card setup for individuals too using MapQuestAPI as google have restricted to one free request a day!

- platform: generic
  name: 360DEVICE_MapQuest
  still_image_url: https://open.mapquestapi.com/staticmap/v4/getplacemap?key=YOURAPIKEY&location={{ states.device_tracker.360DEVICE.attributes.latitude }},{{ states.device_tracker.360DEVICE.attributes.longitude }}&size=800,800&zoom=18&showicon=red_1-1{{ states.device_tracker.360DEVICE.attributes.latitude }},{{ states.device_tracker.360DEVICE.attributes.longitude }}
  limit_refetch_to_url_change: true

I added mine as a seperate tab in my group.yaml

  Family_Location:
    view: yes
    name: "Family Locations"
    entities:
      - camera.mum_MapQuest
      - camera.dad_MapQuest
      - camera.child1_MapQuest
      - camera.child2_MapQuest

The only annoyance I have is that they are laid out in a column! - if you know how to fix that it would be great!

nice indeed! would you happen to know how we could do so with OpenStreetMap too? Ive been trying to do so, but get an unavailable …please see here Reverse Geocode Sensor ("Places") using OpenStreetMap - custom component - #47 by Mariusthvdb not to pollute this thread…

a python question: I like a friendly name for these sensors, (btw one was created indeed) but in this form there are no attributes at all. Usually I do it like this:

if entity_id and state:
    old_state = hass.states.get(entity_id)
    if old_state:
        attrs = old_state.attributes
    else:
        attrs = None
    hass.states.set(entity_id, state,{
      'friendly_name': entity_id.split('.')[1].title().replace('_',' ')
      })

would that be valid, or is taking out the attrs in the sensor definition an issue?

So, just to be clear, this is not a usual way to create entities. In fact, you’re not actually creating an entity, but rather just creating a state in the state machine. I believe this is perfectly valid and acceptable, but it’s not “usual.”

Having said that, if you want to add a friendly_name attribute, I suppose you could in the hass.states.set call. Note that the script was originally written for someone who was trying to change the state (in the state machine) of a real, existing entity. Hence the lines of code to retrieve the existing attributes. But for this use case, the following may be more appropriate (using your technique to create a friendly_name from the object_id):

entity_id = data.get('entity_id')
if not entity_id:
    logger.error('No entity_id provided')
state = data.get('state')
if not state:
    logger.error('No state provided')
if entity_id and state:
    hass.states.set(entity_id, state, {
        'friendly_name': entity_id.split('.')[1].title().replace('_',' ')})

Or if you wanted the script to be more generic, you could pass in the (optional) friendly_name as a variable to the script, in which case:

entity_id = data.get('entity_id')
if not entity_id:
    logger.error('No entity_id provided')
state = data.get('state')
if not state:
    logger.error('No state provided')
# Optional friendly name
friendly_name = data.get('friendly_name')
if entity_id and state:
    attrs = {'friendly_name': friendly_name} if friendly_name else None
    hass.states.set(entity_id, state, attrs)

And you might also want to consider adding a ‘device_class’ attribute. It all depends on what you’re trying to do, and how generic you want the script to be.

I tried with openstreetmap after ditching googlemaps, but i kept getitng the error telling i needed to include the referrer header. - MapQuest give 15,000 free calls to the API a month, so just decided to use that.

You can sign up for a free account on the website.

understand about the creation of a state, and not a true entity. That is why I can’t customize these in the regular way either, and need to set customizations in this attributes set. also use entity_picture, or icon, unit_of_measurement, etc etc.

about you more generic script:
wouldn’t : friendly_name = data.get('friendly_name') imply it already has a friendly_name set, and thats imported here? That wouldn’t be possible would it, since the binary_sensor is created in this script?

btw this is working just great now:

if entity_id and state:
    theme = 'red' if state == 'on' else 'green'
    friendlyName = entity_id.split('.')[1].title().replace('_',' ')

    hass.states.set(entity_id, state,{
      'custom_ui_state_card': 'state-card-custom-ui',
      'friendly_name': friendlyName,
      'device_class': 'problem',
      'theme': theme,
      'show_last_changed': 'true'
      })

48

46

00

No. The variable named data contains the variables passed into the script. So data.get('friendly_name') is getting the value of the variable named friendly_name (or None if that variable doesn’t exist), whereas hass.states.get(entity_id).attributes.get('friendly_name') would get the friendly_name attribute of the specified entity (or None if it doesn’t exist.)

And, BTW, hass.states.set() creates a state if it doesn’t already exist in the state machine, or updates it if it does. So, once you’ve created the state, the next time around you could use hass.states.get() to get its state and attributes.

1 Like

Hi Phil @pnbruckner,

Have a follow-up question, we didn’t touch yet (I think).

Just now, I got a ‘could not be located’ notification from the life360 app. Would this be seen by the component too? I don’t see anything changing in the device_tracker that would reflect that.

I would think this is something else than the time-out aspect we’ve been discussing before. This is an active notification from the platform itself, which would at least be worthwhile picking up in the device_tracker.
As a suggestion, I would like to ask if this could be added to the trigger for the overdue event, or, preferably, create its own event. Because that’s what it is, a could_not_be_located event, which is mirrored by the ‘has_been_located’ event.
would you consider that a valuable feature_request?
My use-case would be that upon receiving that event, it could trigger an automation setting a binary value ‘located’ just as I do now with the ‘overdue’:

  - alias: Life360 connected
    trigger: 
      - platform: event
        event_type: device_tracker.life360_update_overdue
      - platform: event
        event_type: device_tracker.life360_update_restored
    action:
      service: python_script.set_state
      data_template:
        entity_id: >
          binary_sensor.overdue_{{ trigger.event.data.entity_id.split('.')[1] }}
        state: >
          {{ 'on' if trigger.event.event_type.endswith('overdue') else 'off' }}

which could then become

  - alias: Life360 located
    trigger: 
      - platform: event
        event_type: device_tracker.life360_could_not_be_located
      - platform: event
        event_type: device_tracker.life360_has_been_located
    action:
      service: python_script.set_state
      data_template:
        entity_id: >
          binary_sensor.located_{{ trigger.event.data.entity_id.split('.')[1] }}
        state: >
          {{ 'on' if trigger.event.event_type.endswith('located') else 'off' }}

Adding to the above, it would be really nice if we would be allowed to set the values for the device_tracker dynamically, or reworded, by a value_template. I’d love to be able to use an input_number for the max_value_wait, and be able to change that from the frontend . I have no idea if that could even be possible given the constraints of the device_tracker, but it would be a real nice touch, to be able to set all timers with the one slider to change automations sensitivity. As it would for gps accuracy on all device_trackers and zones…

please see this issue on Github:

https://github.com/home-assistant/home-assistant/issues/17356#issuecomment-429283446

could be of interest in relation to our previous talks #last_seen

First, thanks for the ideas. They are appreciated.

Regarding “could not be located”, I see two problems. First, when the Life360 app gives that notification, I don’t know how that is indicated via the REST API. Remember, this API is not documented. When you see that notification via the Life360 app, do you see anything different in the corresponding device_tracker’s state or attributes? Do you see any DEBUG or ERROR messages? Based on what you said it appears not. I’m not sure how to even tell this has happened via the REST API. There may be a way, but it’s not obvious to me what it might be.

The other problem (or at least my opinion) is, I’m not sure this is actually useful. There already is an “update overdue” message (which will presumably also fire at some point when the “could not locate” notification happens), and last_seen will get old. And as you said you’re already getting a notification from the Life360 app.

I guess it might not be terrible to fire another event; it can just be ignored by those that don’t care. And what’s one or two more events in a sea of events which HA is. The biggest problem, though, is I don’t know how to determine when this happens. If you really care about this then private message me and I can try to set you up to analyze the data returned from the Life360 REST API. If you can figure out how this event/condition is indicated by the server, then maybe I could add some code to fire corresponding events.

But, really, is “could not locate” and “update overdue” any different? Aren’t they just cause & effect? I.e., because the device could not be located that’s the cause for the update being overdue???

Yes, I suppose that might be nice. But I believe that would be a lot of work, and I’m not sure it’s worth the effort. How often does anyone actually change this value (I’ve only changed it a few times and have settled on a number I’m happy with), and in the rare instances when you do, how much harder is it to edit a file and restart HA, which is what you’d have to do for just about any config parameter? I wouldn’t mind implementing this if I felt it would actually be used often, but I don’t see that being the case.

The way I read it, that issue really only applies to non-GPS based trackers.

HI,

In the end you could be right. But, and that’s why I asked, if it were to come from the server signaling a ‘could not locate’ it would be better to create an event directly from that, opposed to calculate something like that from the last_seen.
Now if it truly is sent by the server, I wouldn’t know, It’s just that it happened today, and thought it might be good to check.

I now have binary_sensors set up calculating:

  life360_marte_located:
    friendly_name: M 360 located
    value_template: >
      {{ (now() - state_attr('device_tracker.life360_m', 'last_seen'))
         .total_seconds() > 15*60 }}
    device_class: problem

but they behave unexpectedly, compared to the overdue event. Ie, the indicate ok, while the overdue sensors say I have a problem :wink: Ive set the threshold at 15 in both sensor and component, to compare.

Not much harder indeed, much more inconvenient. I have set this up for presence timings ( how much time should pass between triggers)

  - condition: template
    value_template: >
      {{ (now() - trigger.from_state.last_changed | default(0)).total_seconds() > 
                                        states('input_number.presence_timer')|int }}

and lux_thresholds for example, which trigger my lighting automations.

dining_table_lux_input:
  friendly_name: Dining table lux input
  device_class: light
  value_template: >
    {{(states('sensor.dining_table_motion_sensor_lux') | float) < 
      (states('input_number.dining_table_lux') | float)}}

50

Very very useful, being able to play and have immediate effect in the HA response. Once you are used to it you don’t want to miss it any more ;-). Still, I appreciate your efforts remark, and would want to press in any way.

You’ve fallen into the trap that many, many people fall into with template sensors. You can’t expect it to update because now() changes. Template sensors only update when a referenced entity changes, and now() is not an entity (i.e., it does not cause state_changed events.)

The easiest way to fix this is to add the entity_id option and populate it with all the entities in the template, plus sensor.time. (And if you don’t have sensor.time defined, then add it to your config.)

So:

  life360_marte_located:
    friendly_name: M 360 located
    entity_id: device_tracker.life360_m, sensor.time
    value_template: >
      {{ (now() - state_attr('device_tracker.life360_m', 'last_seen'))
         .total_seconds() > 15*60 }}
    device_class: problem

Adding sensor.time explicitly will cause the template sensor to be updated once a minute.

1 Like

of course!
this was a bad cut and paste from another sensor… pardon me please…

Thanks for explaining so eloquently!

so I updated to the release version 0.80 today, and now see this in the log:

2018-10-13 22:39:44 ERROR (MainThread) [homeassistant.core] Error doing job: Exception in callback <bound method BinarySensorTemplate.async_check_state of <Entity M 360 located: off>>
Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 64, in uvloop.loop.Handle._run
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/binary_sensor/template.py", line 199, in async_check_state
    state = self._async_render()
  File "/usr/local/lib/python3.6/site-packages/homeassistant/components/binary_sensor/template.py", line 163, in _async_render
    state = (self._template.async_render().lower() == 'true')
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/template.py", line 132, in async_render
    return self._compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
    return original_render(self, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "/usr/local/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 2, in top-level template code
TypeError: unsupported operand type(s) for -: 'datetime.datetime' and 'NoneType'

binary_sensor:

  life360_m_located:
    friendly_name: M 360 located
    entity_id: device_tracker.life360_m, sensor.time
    value_template: >
      {{ (now() - state_attr('device_tracker.life360_m', 'last_seen'))
         .total_seconds() > 15*60 }}
    device_class: problem

the sensor displays fine in the frontend…

Would you know if anything is wrong here, of if at startup some value hasn’t been initiated yet?

as a followup on the above template sensor, Ive thoroughly rescanned all items. Had a few unexpected front-end errors in my presence-package and since this was the latest change, took them out for debugging purposes.

After all went well, Ive carefully added these back, one by one. So far so good. Since I was restarting HA all the time anyway, I tried the original template sensor once more, since your words kept ringing in my mind:

Without any doubt Ive followed this blindly. Since the main entity in my template is the life360 sensor, and its attribute last_seen, I couldn’t refrain from trying once more. Expecting the template to change if and when that attribute changes. It does in the template editor.

I can’t but conclude the earlier error I reported on their behavior in this configuration must have been caused by something else, because they now show correct states, and do change whenever a problem arises…Have had them running only for a couple of hours now, so maybe need some more time to show the faulty configuration.

could it be that the calculation using the device_tracker is enough for the template to change state correctly?
Only gripe is that at startup it hasn’t initiated the life device_tracker soon enough, so it errors in a non state, as mentioned earlier.

Regarding your template that uses a life360 device_tracker attribute (last_seen) and the now() function, it will update whenever the device_tracker updates, but it won’t update when time (i.e., “now()”) changes enough for the template to evaluate to true. For it to evaluate to true the device_tracker’s last_seen attribute must not change. And if it’s not changing, then the entire device_tracker’s state shouldn’t be changing either. Which means the template won’t update, because the entity hasn’t had a state change. That’s my point – just because time (i.e., now()) changes doesn’t mean the template will update.

But getting back to the point about when the device_tracker entity first updates after restart, yes, this is a problem that I’ve been meaning to do something about for some time. I’ve opened issue #46 which I think I can resolve today.

1 Like

If anyone would like to try it before I officially “release” the change, you can grab a copy here. It was a very simple change which mimics what the Google Maps Location Sharing platform code does.

Released version 1.6.0.

An update immediately after initialization was added. This should cause the resultant device_tracker entities to be as up-to-date as possible after a HA restart, with all their usual attributes.

Previously the entities would be restored from the database. The result was their states would be a bit stale until the first scheduled periodic update, but also they only contained the “standard set” of attributes restored by the device tracker component.

Is anyone else having a problem with the custom_updater (Lovelace card) not working to update the Life360 custom components?

I just now ran it (from Hassio 80.1) and it properly updated the custom_updater to its latest version (3.1.7), but then it did not continue to update any of the Life360 components. The Update button does nothing now. The custom_component entry in the card disappeared from the card as well after it was updated, so I’ll report this apparent bug - not an issue for Phil.

Just pointing it out here for awareness.

EDIT: Issue Reference # - Restarting Hassio after updating the custom_updater restores the card functionality somewhat. It will allow you to update each custom component one at a time, and throws errors in the log each time.