From c04555d7b1d65a2be0e263da8d6659cd2ffa0e7c Mon Sep 17 00:00:00 2001 From: Hydreliox Date: Fri, 4 Mar 2016 20:19:50 +0100 Subject: [PATCH] Add GPS accuracy check for a location update or an event update Add tests related to this Great thanks to @pavoni for his support on this fix --- .../components/device_tracker/owntracks.py | 20 +++++- .../device_tracker/test_owntracks.py | 61 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index 3f430d798a4..7ab59f95262 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -28,10 +28,14 @@ _LOGGER = logging.getLogger(__name__) LOCK = threading.Lock() +CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy' + def setup_scanner(hass, config, see): """ Set up an OwnTracks tracker. """ + max_gps_accuracy = config.get(CONF_MAX_GPS_ACCURACY) + def owntracks_location_update(topic, payload, qos): """ MQTT message received. """ @@ -45,7 +49,9 @@ def setup_scanner(hass, config, see): 'Unable to parse payload as JSON: %s', payload) return - if not isinstance(data, dict) or data.get('_type') != 'location': + if (not isinstance(data, dict) or data.get('_type') != 'location') or ( + 'acc' in data and max_gps_accuracy is not None and data[ + 'acc'] > max_gps_accuracy): return dev_id, kwargs = _parse_see_args(topic, data) @@ -124,12 +130,20 @@ def setup_scanner(hass, config, see): kwargs['location_name'] = new_region _set_gps_from_zone(kwargs, zone) _LOGGER.info("Exit to %s", new_region) + see(**kwargs) + see_beacons(dev_id, kwargs) else: _LOGGER.info("Exit to GPS") + # Check for GPS accuracy + if not ('acc' in data and + max_gps_accuracy is not None and + data['acc'] > max_gps_accuracy): - see(**kwargs) - see_beacons(dev_id, kwargs) + see(**kwargs) + see_beacons(dev_id, kwargs) + else: + _LOGGER.info("Inaccurate GPS reported") beacons = MOBILE_BEACONS_ACTIVE[dev_id] if location in beacons: diff --git a/tests/components/device_tracker/test_owntracks.py b/tests/components/device_tracker/test_owntracks.py index 39914e704a3..4886d02c860 100644 --- a/tests/components/device_tracker/test_owntracks.py +++ b/tests/components/device_tracker/test_owntracks.py @@ -30,6 +30,8 @@ DEVICE_TRACKER_STATE = "device_tracker.{}_{}".format(USER, DEVICE) IBEACON_DEVICE = 'keys' REGION_TRACKER_STATE = "device_tracker.beacon_{}".format(IBEACON_DEVICE) +CONF_MAX_GPS_ACCURACY = 'max_gps_accuracy' + LOCATION_MESSAGE = { 'batt': 92, 'cog': 248, @@ -45,6 +47,21 @@ LOCATION_MESSAGE = { 'tst': 1, 'vel': 0} +LOCATION_MESSAGE_INACCURATE = { + 'batt': 92, + 'cog': 248, + 'tid': 'user', + 'lon': 2.0, + 't': 'u', + 'alt': 27, + 'acc': 2000, + 'p': 101.3977584838867, + 'vac': 4, + 'lat': 6.0, + '_type': 'location', + 'tst': 1, + 'vel': 0} + REGION_ENTER_MESSAGE = { 'lon': 1.0, 'event': 'enter', @@ -70,6 +87,18 @@ REGION_LEAVE_MESSAGE = { 'lat': 2.0, '_type': 'transition'} +REGION_LEAVE_INACCURATE_MESSAGE = { + 'lon': 10.0, + 'event': 'leave', + 'tid': 'user', + 'desc': 'inner', + 'wtst': 1, + 't': 'b', + 'acc': 2000, + 'tst': 2, + 'lat': 20.0, + '_type': 'transition'} + class TestDeviceTrackerOwnTracks(unittest.TestCase): """ Test the Template sensor. """ @@ -80,7 +109,8 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): mock_mqtt_component(self.hass) self.assertTrue(device_tracker.setup(self.hass, { device_tracker.DOMAIN: { - CONF_PLATFORM: 'owntracks' + CONF_PLATFORM: 'owntracks', + CONF_MAX_GPS_ACCURACY: 200 }})) self.hass.states.set( @@ -146,6 +176,10 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): state = self.hass.states.get(DEVICE_TRACKER_STATE) self.assertEqual(state.attributes.get('latitude'), latitude) + def assert_location_longitude(self, longitude): + state = self.hass.states.get(DEVICE_TRACKER_STATE) + self.assertEqual(state.attributes.get('longitude'), longitude) + def assert_location_accuracy(self, accuracy): state = self.hass.states.get(DEVICE_TRACKER_STATE) self.assertEqual(state.attributes.get('gps_accuracy'), accuracy) @@ -169,6 +203,13 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): self.assert_location_accuracy(60.0) self.assert_location_state('outer') + def test_location_inaccurate_gps(self): + self.send_message(LOCATION_TOPIC, LOCATION_MESSAGE) + self.send_message(LOCATION_TOPIC, LOCATION_MESSAGE_INACCURATE) + + self.assert_location_latitude(2.0) + self.assert_location_longitude(1.0) + def test_event_entry_exit(self): self.send_message(EVENT_TOPIC, REGION_ENTER_MESSAGE) @@ -194,6 +235,24 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): # Left clean zone state self.assertFalse(owntracks.REGIONS_ENTERED[USER]) + def test_event_entry_exit_inaccurate(self): + self.send_message(EVENT_TOPIC, REGION_ENTER_MESSAGE) + + # Enter uses the zone's gps co-ords + self.assert_location_latitude(2.1) + self.assert_location_accuracy(10.0) + self.assert_location_state('inner') + + self.send_message(EVENT_TOPIC, REGION_LEAVE_INACCURATE_MESSAGE) + + # Exit doesn't use inaccurate gps + self.assert_location_latitude(2.1) + self.assert_location_accuracy(10.0) + self.assert_location_state('inner') + + # But does exit region correctly + self.assertFalse(owntracks.REGIONS_ENTERED[USER]) + def test_event_exit_outside_zone_sets_away(self): self.send_message(EVENT_TOPIC, REGION_ENTER_MESSAGE) self.assert_location_state('inner')