From 80d2f35cc5511e6091e65d5b268f8034e877f1e3 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 11:49:44 +0000 Subject: [PATCH 1/7] First cut mobile beacon tracking. --- .../components/device_tracker/owntracks.py | 26 +++++- .../device_tracker/test_owntracks.py | 80 ++++++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index d94c8c8c84d..4a8b9083f0a 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -17,6 +17,9 @@ from homeassistant.const import STATE_HOME DEPENDENCIES = ['mqtt'] REGIONS_ENTERED = defaultdict(list) +MOBILE_BEACONS_ACTIVE = defaultdict(list) + +BEACON_DEV_ID = 'beacon' LOCATION_TOPIC = 'owntracks/+/+' EVENT_TOPIC = 'owntracks/+/+/event' @@ -56,6 +59,7 @@ def setup_scanner(hass, config, see): return see(**kwargs) + see_beacons(dev_id, kwargs) def owntracks_event_update(topic, payload, qos): # pylint: disable=too-many-branches @@ -85,7 +89,13 @@ def setup_scanner(hass, config, see): if data['event'] == 'enter': zone = hass.states.get("zone.{}".format(location)) with LOCK: - if zone is not None: + if zone is None: + if data['t'] == 'b': + # Not a HA zone, and a beacon so assume mobile + MOBILE_BEACONS_ACTIVE[dev_id].append(location) + + else: + # Normal region kwargs['location_name'] = location regions = REGIONS_ENTERED[dev_id] @@ -95,6 +105,7 @@ def setup_scanner(hass, config, see): _set_gps_from_zone(kwargs, zone) see(**kwargs) + see_beacons(dev_id, kwargs) elif data['event'] == 'leave': regions = REGIONS_ENTERED[dev_id] @@ -113,6 +124,11 @@ def setup_scanner(hass, config, see): _LOGGER.info("Exit from %s to GPS", location) see(**kwargs) + see_beacons(dev_id, kwargs) + + beacons = MOBILE_BEACONS_ACTIVE[dev_id] + if location in beacons: + beacons.remove(location) else: _LOGGER.error( @@ -120,6 +136,14 @@ def setup_scanner(hass, config, see): data['event']) return + def see_beacons(dev_id, kwargs): + # Live beacons should be set to the same location + _LOGGER.info(MOBILE_BEACONS_ACTIVE) + for beacon in MOBILE_BEACONS_ACTIVE[dev_id]: + kwargs['dev_id'] = beacon + kwargs['host_name'] = BEACON_DEV_ID + see(**kwargs) + mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1) mqtt.subscribe(hass, EVENT_TOPIC, owntracks_event_update, 1) diff --git a/tests/components/device_tracker/test_owntracks.py b/tests/components/device_tracker/test_owntracks.py index 0315894f98a..cc7369dd70d 100644 --- a/tests/components/device_tracker/test_owntracks.py +++ b/tests/components/device_tracker/test_owntracks.py @@ -27,6 +27,9 @@ EVENT_TOPIC = "owntracks/{}/{}/event".format(USER, DEVICE) DEVICE_TRACKER_STATE = "device_tracker.{}_{}".format(USER, DEVICE) +IBEACON_DEVICE = 'keys' +REGION_TRACKER_STATE = "device_tracker.{}".format(IBEACON_DEVICE) + LOCATION_MESSAGE = { 'batt': 92, 'cog': 248, @@ -109,6 +112,7 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): # Clear state between teste self.hass.states.set(DEVICE_TRACKER_STATE, None) owntracks.REGIONS_ENTERED = defaultdict(list) + owntracks.MOBILE_BEACONS_ACTIVE = defaultdict(list) def teardown_method(self, method): """ Stop down stuff we started. """ @@ -136,6 +140,18 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): state = self.hass.states.get(DEVICE_TRACKER_STATE) self.assertEqual(state.attributes.get('gps_accuracy'), accuracy) + def assert_tracker_state(self, location): + state = self.hass.states.get(REGION_TRACKER_STATE) + self.assertEqual(state.state, location) + + def assert_tracker_latitude(self, latitude): + state = self.hass.states.get(REGION_TRACKER_STATE) + self.assertEqual(state.attributes.get('latitude'), latitude) + + def assert_tracker_accuracy(self, accuracy): + state = self.hass.states.get(REGION_TRACKER_STATE) + self.assertEqual(state.attributes.get('gps_accuracy'), accuracy) + def test_location_update(self): self.send_message(LOCATION_TOPIC, LOCATION_MESSAGE) @@ -189,7 +205,6 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): self.assert_location_latitude(2.1) self.assert_location_accuracy(10.0) - # Enter inner2 zone message = REGION_ENTER_MESSAGE.copy() message['desc'] = "inner_2" @@ -198,7 +213,6 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): self.assert_location_latitude(2.1) self.assert_location_accuracy(10.0) - # Exit inner_2 - should be in 'inner' message = REGION_LEAVE_MESSAGE.copy() message['desc'] = "inner_2" @@ -213,7 +227,6 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): self.assert_location_latitude(2.0) self.assert_location_accuracy(60.0) - def test_event_entry_exit_wrong_order(self): # Enter inner zone self.send_message(EVENT_TOPIC, REGION_ENTER_MESSAGE) @@ -259,3 +272,64 @@ class TestDeviceTrackerOwnTracks(unittest.TestCase): self.send_message(EVENT_TOPIC, REGION_ENTER_MESSAGE) self.assert_location_state('inner') + + def test_mobile_enter_move_beacon(self): + # Enter mobile beacon, should set location + message = REGION_ENTER_MESSAGE.copy() + message['desc'] = IBEACON_DEVICE + self.send_message(EVENT_TOPIC, message) + + self.assert_tracker_latitude(2.0) + self.assert_tracker_state('outer') + + # Move should move beacon + message = LOCATION_MESSAGE.copy() + message['lat'] = "3.0" + self.send_message(LOCATION_TOPIC, message) + + self.assert_tracker_latitude(3.0) + self.assert_tracker_state(STATE_NOT_HOME) + + def test_mobile_enter_exit_region_beacon(self): + # Start tracking beacon + message = REGION_ENTER_MESSAGE.copy() + message['desc'] = IBEACON_DEVICE + self.send_message(EVENT_TOPIC, message) + self.assert_tracker_latitude(2.0) + self.assert_tracker_state('outer') + + # Enter location should move beacon + message = REGION_ENTER_MESSAGE.copy() + message['desc'] = "inner_2" + self.send_message(EVENT_TOPIC, message) + + self.assert_tracker_latitude(2.1) + self.assert_tracker_state('inner_2') + + # Exit location should switch to gps + message = REGION_LEAVE_MESSAGE.copy() + message['desc'] = "inner_2" + self.send_message(EVENT_TOPIC, message) + self.assert_tracker_latitude(2.0) + + def test_mobile_exit_move_beacon(self): + # Start tracking beacon + message = REGION_ENTER_MESSAGE.copy() + message['desc'] = IBEACON_DEVICE + self.send_message(EVENT_TOPIC, message) + self.assert_tracker_latitude(2.0) + self.assert_tracker_state('outer') + + # Exit mobile beacon, should set location + message = REGION_LEAVE_MESSAGE.copy() + message['desc'] = IBEACON_DEVICE + message['lat'] = "3.0" + self.send_message(EVENT_TOPIC, message) + + self.assert_tracker_latitude(3.0) + + # Move after exit should do nothing + message = LOCATION_MESSAGE.copy() + message['lat'] = "4.0" + self.send_message(LOCATION_TOPIC, LOCATION_MESSAGE) + self.assert_tracker_latitude(3.0) From 09ef2e1b8c5e85cb424918944881e6e201d22566 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 11:59:16 +0000 Subject: [PATCH 2/7] Add docstring, remove trace. --- homeassistant/components/device_tracker/owntracks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index 4a8b9083f0a..6a55d9a0e0d 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -137,8 +137,8 @@ def setup_scanner(hass, config, see): return def see_beacons(dev_id, kwargs): - # Live beacons should be set to the same location - _LOGGER.info(MOBILE_BEACONS_ACTIVE) + """ Set active beacons to the current location """ + for beacon in MOBILE_BEACONS_ACTIVE[dev_id]: kwargs['dev_id'] = beacon kwargs['host_name'] = BEACON_DEV_ID From 5a7e38039637253c581bb7c5dec7fd1eea06db68 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 15:15:59 +0000 Subject: [PATCH 3/7] Revise beacon name. Add debug. --- .../components/device_tracker/owntracks.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index 6a55d9a0e0d..cf591efdce7 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -52,6 +52,8 @@ def setup_scanner(hass, config, see): # Block updates if we're in a region with LOCK: + _LOGGER.error("REGIONS_ENTERED %s",REGIONS_ENTERED) + _LOGGER.error("MOBILE_BEACONS_ACTIVE %s",MOBILE_BEACONS_ACTIVE) if REGIONS_ENTERED[dev_id]: _LOGGER.debug( "location update ignored - inside region %s", @@ -75,6 +77,10 @@ def setup_scanner(hass, config, see): 'Unable to parse payload as JSON: %s', payload) return + _LOGGER.error("REGIONS_ENTERED %s",REGIONS_ENTERED) + _LOGGER.error("MOBILE_BEACONS_ACTIVE %s",MOBILE_BEACONS_ACTIVE) + + if not isinstance(data, dict) or data.get('_type') != 'transition': return @@ -93,7 +99,6 @@ def setup_scanner(hass, config, see): if data['t'] == 'b': # Not a HA zone, and a beacon so assume mobile MOBILE_BEACONS_ACTIVE[dev_id].append(location) - else: # Normal region kwargs['location_name'] = location @@ -118,10 +123,10 @@ def setup_scanner(hass, config, see): zone = hass.states.get("zone.{}".format(new_region)) kwargs['location_name'] = new_region _set_gps_from_zone(kwargs, zone) - _LOGGER.info("Exit from %s to %s", location, new_region) + _LOGGER.info("Exit from to %s", new_region) else: - _LOGGER.info("Exit from %s to GPS", location) + _LOGGER.info("Exit from to GPS") see(**kwargs) see_beacons(dev_id, kwargs) @@ -140,8 +145,8 @@ def setup_scanner(hass, config, see): """ Set active beacons to the current location """ for beacon in MOBILE_BEACONS_ACTIVE[dev_id]: - kwargs['dev_id'] = beacon - kwargs['host_name'] = BEACON_DEV_ID + kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon) + kwargs['host_name'] = beacon see(**kwargs) mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1) From a3abd8bb085df7f6a3d827cb7802bcacc0eadb23 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 16:12:06 +0000 Subject: [PATCH 4/7] Revise tracked beacon name. --- tests/components/device_tracker/test_owntracks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/components/device_tracker/test_owntracks.py b/tests/components/device_tracker/test_owntracks.py index cc7369dd70d..3563ffebc25 100644 --- a/tests/components/device_tracker/test_owntracks.py +++ b/tests/components/device_tracker/test_owntracks.py @@ -28,7 +28,7 @@ EVENT_TOPIC = "owntracks/{}/{}/event".format(USER, DEVICE) DEVICE_TRACKER_STATE = "device_tracker.{}_{}".format(USER, DEVICE) IBEACON_DEVICE = 'keys' -REGION_TRACKER_STATE = "device_tracker.{}".format(IBEACON_DEVICE) +REGION_TRACKER_STATE = "device_tracker.beacon_{}".format(IBEACON_DEVICE) LOCATION_MESSAGE = { 'batt': 92, From c5cdf6d7cf3f5831ace9ee7db0ab2b2dd05449cf Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 16:12:34 +0000 Subject: [PATCH 5/7] Copy kwargs, remove traces. --- homeassistant/components/device_tracker/owntracks.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index cf591efdce7..c3065142cca 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -52,8 +52,6 @@ def setup_scanner(hass, config, see): # Block updates if we're in a region with LOCK: - _LOGGER.error("REGIONS_ENTERED %s",REGIONS_ENTERED) - _LOGGER.error("MOBILE_BEACONS_ACTIVE %s",MOBILE_BEACONS_ACTIVE) if REGIONS_ENTERED[dev_id]: _LOGGER.debug( "location update ignored - inside region %s", @@ -77,10 +75,6 @@ def setup_scanner(hass, config, see): 'Unable to parse payload as JSON: %s', payload) return - _LOGGER.error("REGIONS_ENTERED %s",REGIONS_ENTERED) - _LOGGER.error("MOBILE_BEACONS_ACTIVE %s",MOBILE_BEACONS_ACTIVE) - - if not isinstance(data, dict) or data.get('_type') != 'transition': return @@ -141,9 +135,10 @@ def setup_scanner(hass, config, see): data['event']) return - def see_beacons(dev_id, kwargs): + def see_beacons(dev_id, kwargs_param): """ Set active beacons to the current location """ + kwargs = kwargs_param.copy() for beacon in MOBILE_BEACONS_ACTIVE[dev_id]: kwargs['dev_id'] = "{}_{}".format(BEACON_DEV_ID, beacon) kwargs['host_name'] = beacon From 03e7ac2a0eddb5f885b35be80e736fc53a703851 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 17:01:13 +0000 Subject: [PATCH 6/7] Fix typo. --- homeassistant/components/device_tracker/owntracks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index c3065142cca..0a924126b11 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -120,7 +120,7 @@ def setup_scanner(hass, config, see): _LOGGER.info("Exit from to %s", new_region) else: - _LOGGER.info("Exit from to GPS") + _LOGGER.info("Exit to GPS") see(**kwargs) see_beacons(dev_id, kwargs) From 515307b4045394a39814ff70a93d3f0f30245eb7 Mon Sep 17 00:00:00 2001 From: pavoni Date: Fri, 29 Jan 2016 17:35:42 +0000 Subject: [PATCH 7/7] Remove dev/null from build script. --- script/cibuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/cibuild b/script/cibuild index 11d91415405..3a8dbd69c6e 100755 --- a/script/cibuild +++ b/script/cibuild @@ -7,7 +7,7 @@ cd "$(dirname "$0")/.." if [ "$TRAVIS_PYTHON_VERSION" = "3.5" ]; then echo "Verifying requirements_all.txt..." - python3 setup.py -q develop 2> /dev/null + python3 setup.py -q develop tput setaf 1 script/gen_requirements_all.py validate VERIFY_REQUIREMENTS_STATUS=$?