Life360 Device Tracker Platform

Hi!

This is my first time loading a custom component in Hass.io. I’m getting an import error:

File “/config/custom_components/device_tracker/life360.py”, line 82, in setup_scanner
from custom_components.life360 import life360
ImportError: cannot import name ‘life360’

That’s referring to this line:
from custom_components.life360 import life360

Folders and such look fine, right in the config folder. Added the platform to configuration.yaml. Am I missing something to get this to load? I’m on 70.1 hass.io fwiw.

Thanks so much for your work here! Life360 is a great tool that I’ve been using with IFTTT for awhile.

So I’m not a Hass.io user, so I’m not sure what’s different about that than the traditional install. Can you verify where your <config> folder is? Look on the Info page (where the version is displayed.) Right under the version it should say something like:

Path to configuration.yaml: /home/homeassistant/.homeassistant

This is where your custom_components folder should be. So, in my case, that would be:

/home/homeassistant/.homeassistant/custom_components

Inside that folder should be the one life360.py file, and then the other life360.py file should be in a folder named device_tracker under custom_components.

So, to summarize, the two files should be located here:

<config>/custom_components/life360.py
<config>/custom_components/device_tracker/life360.py

Where, again <config> is your configuration folder as shown on the Info page.

But, if I had to guess, I’m thinking you only copied one of the life360.py files and forgot the one that goes directly in <config>/custom_components.

1 Like

So I checked the file paths for typos 10 times to make sure that wasn’t it. In Hass.IO the config folder is /config by default, everything looked on the up and up.

And wow. Wow. Wow wow. I never hit save in my editor after I pasted your code. The file was 0KB! Sorry man, all is up and running now! Also, this is awesome.

1 Like

If I had a nickel…

Glad it’s working for you!

1 Like

Small update: Fire new event device_tracker.life360_update_restored if update was overdue and reported, and now a new update is available. Include with event (as wait data item) time since last update.

@pnbruckner thanks for the info you have provided for Life360. I followed you instructions pretty thoroughly and was having great success with this implementation the something happen. I am getting constant errors in my log file see below. If you have any ideas or directions to correct this I would love to hear them.

Thanks for all of you help with this.

[custom_components.device_tracker.life360] TypeError: ‘<’ not supported between instances of ‘float’ and ‘NoneType’; m = {‘features’: {‘device’: ‘1’, ‘smartphone’: ‘1’, ‘nonSmartphoneLocating’: ‘0’, ‘geofencing’: ‘1’, ‘shareLocation’: ‘1’, ‘shareOffTimestamp’: None, ‘disconnected’: ‘0’, ‘pendingInvite’: ‘0’, ‘mapDisplay’: ‘1’}, ‘issues’: {‘disconnected’: ‘0’, ‘type’: None, ‘status’: None, ‘title’: None, ‘dialog’: None, ‘action’: None, ‘troubleshooting’: ‘0’}, ‘location’: {‘latitude’: ', ‘longitude’: ‘-84.764356653657’, ‘accuracy’: ‘65’, ‘startTimestamp’: 1529596089, ‘endTimestamp’: ‘1529598496’, ‘since’: 1529596089, ‘timestamp’: ‘1529598496’, ‘name’: None, ‘placeType’: None, ‘source’: None, ‘sourceId’: None, ‘address1’: ‘, ‘address2’: ‘’, ‘shortAddress’:r’, ‘inTransit’: ‘0’, ‘tripId’: None, ‘driveSDKStatus’: None, ‘battery’: ‘10’, ‘charge’: ‘0’, ‘wifiState’: ‘1’, ‘speed’: -1, ‘isDriving’: ‘0’}, ‘communications’: [{‘channel’: ‘Voice’, ‘value’: ‘+’, ‘type’: ‘Home’}, {‘channel’: ‘Email’, ‘value’: ', ‘type’: ‘’}], ‘medical’: None, ‘relation’: None, ‘createdAt’: ‘1353633952’, ‘activity’: None, ‘id’: ‘5c2bdeb1-241c-404f-8da7-894f039a77ea’, ‘firstName’: ', ‘lastName’: ‘r’, ‘loginEmail’: ‘’, ‘loginPhone’: ‘+’, ‘avatar’: ‘https://www.life360.com/img/user_images/5c2bdeb1-241c-404f-8da7-894f039a77ea/1ada36f2-b1b9-4223-8bf8-78999825adea.png?fd=2’, ‘isAdmin’: ‘0’, ‘pinNumber’: None}
2018-06-21 12:37:33 ERROR (SyncWorker_14) [custom_components.device_tracker.life360] TypeError: ‘<’ not supported between instances of ‘float’ and ‘NoneType’; m = {‘features’: {‘device’: ‘1’, ‘smartphone’: ‘1’, ‘nonSmartphoneLocating’: ‘0’, ‘geofencing’: ‘1’, ‘shareLocation’: ‘1’, ‘shareOffTimestamp’: None, ‘disconnected’: ‘0’, ‘pendingInvite’: ‘0’, ‘mapDisplay’: ‘1’}, ‘issues’: {‘disconnected’: ‘0’, ‘type’: None, ‘status’: None, ‘title’: None, ‘dialog’: None, ‘action’: None, ‘troubleshooting’: ‘0’}, ‘location’: {‘latitude’: ‘36.2633994’, ‘longitude’: ‘-76.1736316’, ‘accuracy’: ‘50’, ‘startTimestamp’: 1529598158, ‘endTimestamp’: ‘1529599049’, ‘since’: 1529598158, ‘timestamp’: ‘1529599049’, ‘name’: None, ‘placeType’: None, ‘source’: None, ‘sourceId’: None, ‘address1’: None, ‘address2’: ‘’, ‘shortAddress’: ‘’, ‘inTransit’: ‘0’, ‘tripId’: None, ‘driveSDKStatus’: None, ‘battery’: ‘82’, ‘charge’: ‘0’, ‘wifiState’: ‘1’, ‘speed’: 0, ‘isDriving’: ‘0’}, ‘communications’:

I discovered my last change caused a problem.

The device_tracker.life360_update_restored event’s wait data item is a datetime.timedelta. That causes a problem for the recorder, because a timedelta is not JSON serializable, and all event data needs to be serializable for the recorder. This resulted in the following:

  1. An error message that looks like this: 2018-06-21 15:55:22 ERROR (Recorder) [homeassistant.components.recorder.util] Error executing query: datetime.timedelta(0, 13247) is not JSON serializable
  2. The recorder seems to stop recording when this happens (i.e., logbook, history, graphs, etc. just stop updating.)
  3. An error like this when restarting HA: 2018-06-27 19:14:28 WARNING (Recorder) [homeassistant.components.recorder] Ended unfinished session (id=4 from 2018-06-21 02:42:34.704666)

I’ve made a small change to convert the timedelta to a string and I’m testing now. I should have an update shortly. Sorry for any inconvenience!

Ok, fix has been tested and updated code (custom_components/device_tracker/life360.py) is in github.

1 Like

This is awesome @pnbruckner thank you so much for all your work on this. It so much cleaner than previous mqtt solutions I was using.

Is there any way to filter based on life360 circles or by user? I only really need to track my wife and myself for presence detection. However I have my extended family in another circle, and some of the wives have a habit of turning off their location (I don’t even want to know! :roll_eyes: :joy:). The result is an error to the log every time it polls:

2018-07-10 17:10:57 ERROR (SyncWorker_5) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:10:58 ERROR (SyncWorker_5) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:10:58 ERROR (SyncWorker_5) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:11:09 ERROR (SyncWorker_9) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:11:09 ERROR (SyncWorker_9) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:11:09 ERROR (SyncWorker_9) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:11:19 ERROR (SyncWorker_2) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:11:19 ERROR (SyncWorker_2) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:11:19 ERROR (SyncWorker_2) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:11:30 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:11:31 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:11:31 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:11:41 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:11:42 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:11:42 ERROR (SyncWorker_7) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:11:52 ERROR (SyncWorker_0) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
2018-07-10 17:11:52 ERROR (SyncWorker_0) [custom_components.device_tracker.life360] life360_bex_: Location information missing
2018-07-10 17:11:52 ERROR (SyncWorker_0) [custom_components.device_tracker.life360] life360_phoebe_evans: Location information missing
2018-07-10 17:12:03 ERROR (SyncWorker_2) [custom_components.device_tracker.life360] life360_steph_: Lost Connection: This user has turned off Location Services, which prevents us from updating their location
. . . 

Alternatively just a filter that ignores users that have turned off location services!

Good ideas. I have a rather simple Life360 configuration - one circle with three people, all of which I care about with respect to HA (and who leave their phones on :wink:), so I haven’t run into this sort of thing.

I don’t think I’d want to filter out “Location information missing” because I do see that from time to time. There are also some errors from time to time if the server doesn’t respond, etc.

Probably would make sense to have either a member include list, or a member exclude list. And of those options a member include list probably makes more sense (for several reasons.) Let me work on adding an optional member include list. Would probably have to be a list containing first & last names as defined in Life360.

1 Like

Sounds like a plan!! :grin:

Ok, I’ve made the change to custom_components/device_tracker/life360.py in github and did some minimal testing. Give it a whirl and let me know how it works for you.

I added an optional members parameter. If it’s not specified, then it works as before. If it is, then it should be a list of member names, each in the format first, last (with extra spaces before/after first and last names stripped.) If the member’s name in Life360 is only the first or last, then leave the other blank, but keep the comma, like first, or ,last. So it might look something like this:

device_tracker:
  - platform: life360
    ...
    members:
      - first1, last1
      - first2,
      - ,last3

I also added some extra debug output, so if you do the following:

tail -f home-assistant.log | grep -e Checking -e 'members ='

You should see something like:

2018-07-10 16:00:02 DEBUG (Thread-13) [custom_components.device_tracker.life360] members = ['Phillip, Bruckner']
2018-07-10 16:00:02 DEBUG (Thread-13) [custom_components.device_tracker.life360] processed members = [('Phillip', 'Bruckner')]
2018-07-10 16:00:13 DEBUG (Thread-11) [custom_components.device_tracker.life360] Checking members.
2018-07-10 16:00:13 DEBUG (Thread-11) [custom_components.device_tracker.life360] Checking "Phillip, Bruckner".
2018-07-10 16:00:24 DEBUG (Thread-2) [custom_components.device_tracker.life360] Checking members.
2018-07-10 16:00:24 DEBUG (Thread-2) [custom_components.device_tracker.life360] Checking "Phillip, Bruckner".

Once I know how these changes work I’ll probably get rid of the extra debug logging (at least the “Checking…” lines), because it’s a lot.

Result!! :grin:

Once I realized that the names are case sensitive - it worked like a charm. DEBUG was useful as a reference, and to confirm, the format you suggested for users with only a first name in Life360 works perfectly.

And no more bloated logs!! Thanks @pnbruckner !

Let me know if I can help testing anything else.

1 Like

Hmm, good point. I wonder if Life360 treats the names as case insensitive. I.e., would it allow two circle members with the same name except for case??? I’d guess no. Maybe I should make it case insensitive. Hmm, something to think about…

In any case, glad it works for you.

Maybe you can tell me - when you have more than one circle, can you have the same person in more than one? I’d guess yes. And, if so, then my code would probably update a member that is in multiple circles, multiple times, once for each circle the member is in. Hmm, probably another refinement I could make if this is the case. Actually, I’m wondering if you have that situation. And, if so, do you see the member that’s in multiple circles getting updated multiple times per poll cycle?

The only user I have in more than one circle at the moment is myself - but the debug log would seem to bear out your theory . . .

2018-07-11 06:22:25 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking members.
2018-07-11 06:22:25 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:26 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:26 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:26 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mandy, ".
2018-07-11 06:22:36 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking members.
2018-07-11 06:22:36 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:37 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:37 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:37 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mandy, ".
2018-07-11 06:22:47 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking members.
2018-07-11 06:22:47 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:48 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:48 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:22:48 DEBUG (SyncWorker_6) [custom_components.device_tracker.life360] Checking "Mandy, ".
2018-07-11 06:22:58 DEBUG (SyncWorker_5) [custom_components.device_tracker.life360] Checking members.
2018-07-11 06:22:59 DEBUG (SyncWorker_5) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:23:00 DEBUG (SyncWorker_5) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:23:00 DEBUG (SyncWorker_5) [custom_components.device_tracker.life360] Checking "Mark, Davies".
2018-07-11 06:23:00 DEBUG (SyncWorker_5) [custom_components.device_tracker.life360] Checking "Mandy, ".

As you probably guessed . . . I’m in three circles!

Thanks! I’ll work on optimizing for this situation. It’s probably fairly common. I’ll post back here when I have an update if you wouldn’t mind trying it out.

custom_components/device_tracker/life360.py updated, tested and in github. Member names are now case insensitive. A member that is in multiple circles is now only checked once per cycle. (I use the unique member id for this in case there are multiple members with exactly the same name - like father & son. :slight_smile:) “Checking…” debug messages have been commented out; feel free to uncomment them if you’d like to see more detail.

2018-07-15 05:37:10 DEBUG (SyncWorker_3) [custom_components.device_tracker.life360] Checking members
2018-07-15 05:37:10 DEBUG (SyncWorker_3) [custom_components.device_tracker.life360] Checking "Mark, Davies"
2018-07-15 05:37:10 DEBUG (SyncWorker_3) [custom_components.device_tracker.life360] Checking "Mandy, "
2018-07-15 05:37:20 DEBUG (SyncWorker_8) [custom_components.device_tracker.life360] Checking members
2018-07-15 05:37:20 DEBUG (SyncWorker_8) [custom_components.device_tracker.life360] Checking "Mark, Davies"
2018-07-15 05:37:20 DEBUG (SyncWorker_8) [custom_components.device_tracker.life360] Checking "Mandy, "
2018-07-15 05:37:30 DEBUG (SyncWorker_4) [custom_components.device_tracker.life360] Checking members
2018-07-15 05:37:31 DEBUG (SyncWorker_4) [custom_components.device_tracker.life360] Checking "Mark, Davies"
2018-07-15 05:37:32 DEBUG (SyncWorker_4) [custom_components.device_tracker.life360] Checking "Mandy, "
2018-07-15 05:37:41 DEBUG (SyncWorker_0) [custom_components.device_tracker.life360] Checking members
2018-07-15 05:37:42 DEBUG (SyncWorker_0) [custom_components.device_tracker.life360] Checking "Mark, Davies"
2018-07-15 05:37:42 DEBUG (SyncWorker_0) [custom_components.device_tracker.life360] Checking "Mandy, "

Perfect @pnbruckner This has been working great for the past few days. Much improved presence detection - thank you so much!

As you indicated, case sensitivity is now off, and users in multiple circles are only updated once per poll.

1 Like

Can an option be added to change from the default behavior so that the entities’ states are updated every time the polling occurs, regardless of if it’s same/different? Or maybe provide code to change it.

The reason I ask, is that I use multiple device tracker platforms (as fail-safe/backup). HA is capable of using multiple device trackers to update a single entity. It will display the state of the last updated tracker. With the default behavior of your component, if my router were to lose connection to a member’s phone, it would update as not_home and cause the entity to show Away. And, although Life360 polls a few seconds later and finds the member actually home, the entity does not update to the correct status based on Life360, since the component does not report it due to the location not having actually changed.

The easiest way to do that would be to change the following line in _update_member:

elif prev_update is None or last_update > prev_update:

to:

else:
1 Like