OwnTracks work. Beacon logic and testcases (#10183)
* OwnTracks work. Beacon logic and testcases The existing test cases don't really make clear what is being tested and the iBeacon / Region / Zone / Tracker thing is all a bit confused. I'm distinguishing a fixed-place beacon used to trigger entrance into an HA zone (as a Region Beacon) from a beacon affixed to a portable or mobile object (as a Mobile Beacon). The behaviors and test cases for those usages should be different. A Region Beacon will be named the same as a Home Assistant Zone and seeing an event from that beacon should trigger a device tracker update related to that zone. It would be appropriate, though unnecessary, to configure the Region Beacon with the GPS coordinates of its static physical location. A Mobile Beacon is not named after any HA Zone and seeing the beacon triggers an update in HA setting the location of the beacon to the current device_tracker location. In this way, when my_phone sees the beacon on my_keys, the location of my_keys is set to where my_phone is. And when my_phone stops seeing my_keys, my_keys location is the location of my_phone the last time it saw them. A Mobile Beacon's GPS information should be ignored because it's almost certain to be incorrect because the beacon moves. In fact, beacons typcially come configured with lat/lon as 0.0/0.0 so using the location of the beacon in an update has a nasty habit of setting you and your keys on the bottom of the Atlantic Ocean. Leave message handling is changed to treat mobile beacons differently from region beacons and gps regions. active beacons should be a set. you shouldn't end up with multiple "active" entries for the same beacon. Let's enforce that with the correct data structure. Added test for real-world bug that is fixed. A series of mobile beacon and region beacon enter and leave events could cause a mobile beacon to stick to the tracking device even though it had tracked through a "leave" event. Changed two tests to look at the size of the 'mobile_beacons_active' structure rather than at the object which will allow this test to work with any sort of list, set, etc. * Removing excess logging and unnecessary try catch. From review on PR #10183 I've removed some info logging that was unnecessary and I've made the suggested changes to an if block and a try/catch.
This commit is contained in:
parent
062fb7ac4c
commit
ed3f7d1581
2 changed files with 794 additions and 312 deletions
|
@ -199,7 +199,7 @@ class OwnTracksContext:
|
|||
self.async_see = async_see
|
||||
self.secret = secret
|
||||
self.max_gps_accuracy = max_gps_accuracy
|
||||
self.mobile_beacons_active = defaultdict(list)
|
||||
self.mobile_beacons_active = defaultdict(set)
|
||||
self.regions_entered = defaultdict(list)
|
||||
self.import_waypoints = import_waypoints
|
||||
self.waypoint_whitelist = waypoint_whitelist
|
||||
|
@ -234,10 +234,25 @@ class OwnTracksContext:
|
|||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_see_beacons(self, dev_id, kwargs_param):
|
||||
def async_see_beacons(self, hass, dev_id, kwargs_param):
|
||||
"""Set active beacons to the current location."""
|
||||
kwargs = kwargs_param.copy()
|
||||
|
||||
# Mobile beacons should always be set to the location of the
|
||||
# tracking device. I get the device state and make the necessary
|
||||
# changes to kwargs.
|
||||
device_tracker_state = hass.states.get(
|
||||
"device_tracker.{}".format(dev_id))
|
||||
|
||||
if device_tracker_state is not None:
|
||||
acc = device_tracker_state.attributes.get("gps_accuracy")
|
||||
lat = device_tracker_state.attributes.get("latitude")
|
||||
lon = device_tracker_state.attributes.get("longitude")
|
||||
kwargs['gps_accuracy'] = acc
|
||||
kwargs['gps'] = (lat, lon)
|
||||
|
||||
# the battery state applies to the tracking device, not the beacon
|
||||
# kwargs location is the beacon's configured lat/lon
|
||||
kwargs.pop('battery', None)
|
||||
for beacon in self.mobile_beacons_active[dev_id]:
|
||||
kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon)
|
||||
|
@ -261,7 +276,7 @@ def async_handle_location_message(hass, context, message):
|
|||
return
|
||||
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -271,11 +286,15 @@ def _async_transition_message_enter(hass, context, message, location):
|
|||
dev_id, kwargs = _parse_see_args(message)
|
||||
|
||||
if zone is None and message.get('t') == 'b':
|
||||
# Not a HA zone, and a beacon so assume mobile
|
||||
# Not a HA zone, and a beacon so mobile beacon.
|
||||
# kwargs will contain the lat/lon of the beacon
|
||||
# which is not where the beacon actually is
|
||||
# and is probably set to 0/0
|
||||
beacons = context.mobile_beacons_active[dev_id]
|
||||
if location not in beacons:
|
||||
beacons.append(location)
|
||||
beacons.add(location)
|
||||
_LOGGER.info("Added beacon %s", location)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
else:
|
||||
# Normal region
|
||||
regions = context.regions_entered[dev_id]
|
||||
|
@ -283,9 +302,8 @@ def _async_transition_message_enter(hass, context, message, location):
|
|||
regions.append(location)
|
||||
_LOGGER.info("Enter region %s", location)
|
||||
_set_gps_from_zone(kwargs, location, zone)
|
||||
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
@ -297,8 +315,13 @@ def _async_transition_message_leave(hass, context, message, location):
|
|||
if location in regions:
|
||||
regions.remove(location)
|
||||
|
||||
beacons = context.mobile_beacons_active[dev_id]
|
||||
if location in beacons:
|
||||
beacons.remove(location)
|
||||
_LOGGER.info("Remove beacon %s", location)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
else:
|
||||
new_region = regions[-1] if regions else None
|
||||
|
||||
if new_region:
|
||||
# Exit to previous region
|
||||
zone = hass.states.get(
|
||||
|
@ -306,21 +329,15 @@ def _async_transition_message_leave(hass, context, message, location):
|
|||
_set_gps_from_zone(kwargs, new_region, zone)
|
||||
_LOGGER.info("Exit to %s", new_region)
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
return
|
||||
|
||||
else:
|
||||
_LOGGER.info("Exit to GPS")
|
||||
|
||||
# Check for GPS accuracy
|
||||
if context.async_valid_accuracy(message):
|
||||
yield from context.async_see(**kwargs)
|
||||
yield from context.async_see_beacons(dev_id, kwargs)
|
||||
|
||||
beacons = context.mobile_beacons_active[dev_id]
|
||||
if location in beacons:
|
||||
beacons.remove(location)
|
||||
_LOGGER.info("Remove beacon %s", location)
|
||||
yield from context.async_see_beacons(hass, dev_id, kwargs)
|
||||
|
||||
|
||||
@HANDLERS.register('transition')
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue