Composite Device Tracker Platform

As I just mentioned, I didn’t do anything to prevent the error messages (and they should be warnings, not errors.) The change just makes it so those invalid device_tracker states at startup don’t prevent the composite device from being updated by future state_changed events from the offending device_tracker(s.) If you’re not seeing the “unsupported source_type” messages, then what changed is the source of the errors, not the composite device’s handling of them.

EDIT: In any case, thanks for testing! :smile: Let me know if the behavior changes. Based on @juan11perez’s and your testing I’ll go ahead and release the change.

Released version 1.5.1.

Log, but otherwise ignore, invalid states of watched entities during init. Improve “skipping” debug message.

Well, the only thing changed on this side is your component. Maybe your component was only the messenger, but the messages are gone…

Most welcome, happy to oblige.

Ok. Maybe it’s some sort of race condition. Can’t really explain it. In any case, I’m glad it’s working for you now. If things change, don’t hesitate to let me know.

coming to think of it, I am using the beta channel, so maybe a newer version of the hassio supervisor changed something? I am not sure what that’s responsible for, so if this is not making sense, please forget I mentioned it

I confirm composite_tracker is reading from both my gpslogger and life360.
Once again Thank you very much for the release and taking the time to address the feedback.

1 Like

I love this component, I’ve been using it for several weeks. When I 1st started using it I went all in, and update most of my automations to use this. I’ve pulled back, not because of anything wrong with this sensor, but issues with the trackers I used with it. I just wanted to put this out there for refrence, or maybe something I could do better.

My issue is mostly with gps trackers being inconsistent, causing this to go to home/away/home again. My GPS based trackers are your awesome life360 tracker and the HA iOS app. Here is my most common situation

Wife leaves work, one of the trackers triggers to away, the other one stays at the work zone. Her commute is only about 10 minutes.

She get’s home. The 1st tracker changes to Home, the Composite changes to Home, then about a minute or so later the second tracker changes to away, then a min or so later changes to home. So the composite tracker flips to away and then to home It happens so close to the arrival time it messes with my “welcome home” automations. I do have other binary items added as well, but the composite uses “last reported”, which makes sense, but in my case causes issues.

I still use this for zone reporting, but not for my welcome home sort of automations. I want to be clear, I love this component, if there is anything different I could be doing let me know. My wife’s phone is an older iPhone 6s, and always seems to just be a bit off. I never have these issues with a newer iPhone.

When you say you use HA iOS app for a device tracker, do you mean iOS Hub or iCloud?

In either case, I don’t use it, so I’m not familiar with its details. Can you share what the corresponding device_tracker’s state looks like? Feel free to redact any personal/sensitive data.

I’d be happy to enhance the composite tracker to more properly use the location information from this tracker if possible. And, by that, I mean use a more accurate timestamp for the location data if possible. I suspect that is at the root of the problem. I.e., it’s seeing an update from this tracker, but since it doesn’t know how/where to get a valid timestamp for the location information, it’s just defaulting to using the state’s last_updated field, and assuming that must be valid (which apparently it isn’t.) If it uses the wrong time, then it won’t know to ignore the location information if it’s actually older than the most recent location information its used from another watched tracker.

Sure, I do mean the iOS hub.

basics are
source type: gps
Lotitude:
Longitude:
GPS accuracy
Battery

Just the basics.

I will say that the last time I had the bouncing issue, the iOS app updated correctly and the Life 360 was slow to update. That’s not the norm, but it happens. Either way though, it’s a hard call.

So there’s no attribute with a time or timestamp that indicates when the longitude/latitude were obtained/valid? If not, then like I said, the composite tracker has no choice but to use the device_tracker’s state’s last_updated field and assume that’s when the location was valid.

The Life360 trackers can definitely be slow to update at times, but the updates include a timestamp as to when the location was valid. So if the composite tracker sees an update from Life360, but the location timestamp is older than the last valid location information used, it will just be ignored (and you’ll see a “skipping” debug message in the log.)

The only way (I think) you can see what you’re seeing is that the iOS app is causing its corresponding device_tracker to update, but with location data that is older than what was obtained via Life360 (assuming those are the only GPS-based trackers you’re using.)

FWIW, I’ve been trying to figure out how the iOS app updates the corresponding device_tracker, but the code is a bit complex and I haven’t figured it out yet.

Yeah, I just double checked and there is not a time stamp (or similar) attribute.

it’s definitely not a huge show stopper for me at the moment. I have a thought, and this might be more of a people issue vs a technical one. Maybe the reason my wife has these issue more often than I do is because her commute is so short. Often under 10 minutes. I’ve seen the iOS app as well as the Life 360 tracker take longer provide an update. Not much to do for that. I don’t want to have to tell her to open the apps when she’s leaving work, etc.

The only thing I could think of, is if you have more than 2 device trackers, and two have updated the composite tracker to a zone within a very short period of time (like under a minute), and a third jumps in with a different state/zone, maybe do some analysis? Just a thought.

The problem (or, at least, this problem) is not the amount of time between updates. The problem is an update that implies the location information is valid now when in fact it hasn’t been valid for a significant amount of time.

Both Life360 and Google Maps Location Sharing include timestamps that indicate when the location information was valid. (And from what I’ve experienced, these timestamps seem valid.) For these the composite tracker can decide whether to use an update from one of them, or to discard it if it’s older (or at least not newer) than the last location information that was used.

But for the iOS based device_tracker, it would appear that it provides updates with significantly old location information (and by “significant”, I mean older than a valid/accurate update from another tracker), and worse, provides no indication of how old the information is. (I haven’t verified this is true. I’m concluding this based on what you’ve told me.)

So, for example, if Life360 says your device has entered the Home zone (and it has), and then iOS says your device is still outside the Home Zone (and implies that location is valid now, even though it isn’t), there’s not a whole lot the composite tracker can do about that. And trying to come up with some generic algorithm (other than timestamps) that attempts to ignore what otherwise appears as valid updates, would not be easy.

I’d still like to do something about this if possible, because I suspect you’re not the only one using the iOS based tracker. :wink: But so far I still can’t seem to figure out how the corresponding device_tracker is getting updated. (I see no file in components/device_tracker, other than icloud.py, that seems to be associated with the ios component. And so far I can’t make any connection between components/ios.py and a device_tracker.) If you, or anyone using this component, has any clues as to what code is involved, I’d appreciate it. E.g., if you look in home-assistant.log and see a state_changed event for the iOS-base device_tracker entity, what do you see immediately above that that might imply what code is involved?

1 Like

HI,

maybe this is of help, but excuse me if its not:

I find the HA app one of the more reliable and swift device_trackers, but only if one allows it to track all the way and always. The downside of that is, it eats the battery quite severely. which is reason not to allow it to do all that (takes 3 full battery loads per day, so really not an option)

since we’re talking composite tracker here, those 2 gps trackers mentioned would optimally be combined with 1 or better 2 wifi/bluetooth trackers that are rather swift, at least in deciding if one is home.

setting the radius and playing with each devices consider_home settings is really important too and is different for each tracker…

I am a big fan of the iTunes tracker on ios. It is bulletproof and works much better than a ping or nmap based tracker, but the downside is you want it at a fairly low frequency as it will impact battery life.

So I have it setup also as a triggered script so when a door opens or closes at our house the following happens:

  1. If we’re marked not home using the composite tracker; it runs a poll to see if we are in fact home; which then updates the composite tracker.

  2. If we’re marked at home when the door cycle occurs it starts a trio of timers; and at the end of each timer if we’re still marked at home; it polls. The timers are 1, 3 and 10 minutes.

This has gotten us to a point with very fast home/away detection and 100% reliability.

I also use HomeKit trigger and the IOS app with beacons; but those combined were never 100% in all cases. With the iTunes “poll” it has taken it to 100%; without running that tracker 24/7 at a high frequency draining the battery.

PS: the iTunes method uses a special “wake up” message that brings any iOS device out of sleep to respond; and it’s used by iTunes for it’s wifi sync process (if enabled). So this method basically wakes the iOS device up to ask if it’s present.

1 Like

@pnbruckner,

Hi,
I know that this tales the battery level from Life360 but does it also use Google’s recently available ‘battery level’?
And do you intend to include the charging state of phones now that it is included in both Life360 and Google?

not sure what that is, iTunes tracker? Is that something else than the iCloud tracker we can use when logged into iCloud?
If the same, I find these horribly incorrect. Ive have disable these for their battery consumption, which took 3 full loads per day, so really not an option tbh.

If the same, I would be very interested in your script, because I have always understood the polling is controlled by iCloud and can’t be set manually on demand, other than the global settings in the iCloud of max_interval and iCloud_set_interval Apple iCloud - Home Assistant ?

No, not iCloud. That is far too heavy. All iOS devices have a secret magic way to poll if they are present as iTunes does this when you turn on the wireless sync with a device. So the tracker uses this same mechanism to see if devices are present. This is a UDP message sent to port 5353 on the iOS device.

The challenge is you need to manually set your iOS devices to have static IP addresses (or DHCP reservations) and also need to turn that devices wireless iTunes sync on in iTunes.

Power hit is about 3% per day once this tracker was added. I tested by leaving phone sit idle with cellular turned off just on WIFI. I measured the % drop over 3 days without the tracker and also with the tracker. The tracker showed a 9% additional drop over 3 days. Not super scientific.

Tracker is below:

"""
Tracks iPhones by sending a udp message to port 5353.
An entry in the arp cache is then made and checked.

device_tracker:
  - platform: iphonedetect
    hosts:
      host_one: 192.168.2.12
      host_two: 192.168.2.25
"""
import logging
import subprocess
import sys
from datetime import timedelta
import socket

import voluptuous as vol

from homeassistant.components.device_tracker import (
    PLATFORM_SCHEMA, DEFAULT_SCAN_INTERVAL, SOURCE_TYPE_ROUTER)
from homeassistant.helpers.event import track_point_in_utc_time
from homeassistant import util
from homeassistant import const
import homeassistant.helpers.config_validation as cv

DEPENDENCIES = []

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(const.CONF_HOSTS): {cv.string: cv.string},
})

class Host:
    """Host object with arp detection."""

    def __init__(self, ip_address, dev_id, hass, config):
        """Initialize the Host."""
        self.hass = hass
        self.ip_address = ip_address
        self.dev_id = dev_id

    def detectiphone(self):
        """Send udp message to port 5353 
           and return True if an arp chache entry is made success.
        """
        aSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        aSocket.settimeout(1)
        addr = (self.ip_address, 5353)
        message = b'Steve Jobs'
        aSocket.sendto(message, addr)
    
        try:
            output = subprocess.check_output('arp -na', shell=True)
            output = output.decode('utf-8').split('\n')
            for entry in output:
                mac = entry.split(' ')
                if mac[0] != '':
                    rcvd_ip = mac[1]
                    rcvd_ip = rcvd_ip[:-1]  # remove last Klammer
                    rcvd_ip = rcvd_ip[1:]  # remove first Klammer
                    mac = mac[3]
                    mac = mac.split(':')
                    if rcvd_ip == self.ip_address:
                        if len(mac) == 6:
                            return True
        except subprocess.CalledProcessError:
            return False
        return False

    def update(self, see):
        """Update device state."""
        if self.detectiphone():
            see(dev_id=self.dev_id, source_type=SOURCE_TYPE_ROUTER)
            return True
        _LOGGER.debug("iPhone on ip=%s not present", self.ip_address)

def setup_scanner(hass, config, see, discovery_info=None):
    """Setup the Host objects and return the update function."""
    hosts = [Host(ip, dev_id, hass, config) for (dev_id, ip) in
             config[const.CONF_HOSTS].items()]
    interval = DEFAULT_SCAN_INTERVAL
    _LOGGER.info("Started iphonedetect with interval=%s on hosts: %s",
                 interval, ",".join([host.ip_address for host in hosts]))

    def update(now):
        """Update all the hosts on every interval time."""
        for host in hosts:
            host.update(see)
        track_point_in_utc_time(hass, update, now + interval)
        return True

    return update(util.dt.utcnow())
1 Like

ok cool!
never new about this, will test soon :wink:
have reservations already set, so this would be a small extra.
no other ramifications using this script? btw is this a custom_component?

Yes, sorry, custom component. Name it iphonedetect.py

No drawbacks, I also turned off unifi tracker as this one works better.

ok thanks. anything in the configuration files to call it, or is it on its own. Also, where is it located? config/custom_components/device_tracker ?