diff --git a/homeassistant/components/device_tracker/__init__.py b/homeassistant/components/device_tracker/__init__.py index bb0ac890d1a..c62a433f230 100644 --- a/homeassistant/components/device_tracker/__init__.py +++ b/homeassistant/components/device_tracker/__init__.py @@ -21,6 +21,8 @@ device_tracker: # Maximum distance from home we consider people home range_home: 100 """ +# pylint: disable=too-many-instance-attributes, too-many-arguments +# pylint: disable=too-many-locals import csv from datetime import timedelta import logging @@ -75,6 +77,7 @@ ATTR_DEV_ID = 'dev_id' ATTR_HOST_NAME = 'host_name' ATTR_LOCATION_NAME = 'location_name' ATTR_GPS = 'gps' +ATTR_BATTERY = 'battery' DISCOVERY_PLATFORMS = { discovery.SERVICE_NETGEAR: 'netgear', @@ -250,12 +253,13 @@ class DeviceTracker(object): class Device(Entity): """ Tracked device. """ - # pylint: disable=too-many-instance-attributes, too-many-arguments host_name = None location_name = None gps = None + gps_accuracy = 0 last_seen = None + battery = None # Track if the last update of this device was HOME last_update_home = False @@ -289,8 +293,9 @@ class Device(Entity): @property def gps_home(self): """ Return if device is within range of home. """ - return (self.gps is not None and - self.hass.config.distance(*self.gps) < self.home_range) + distance = max( + 0, self.hass.config.distance(*self.gps) - self.gps_accuracy) + return self.gps is not None and distance <= self.home_range @property def name(self): @@ -311,8 +316,11 @@ class Device(Entity): attr[ATTR_ENTITY_PICTURE] = self.config_picture if self.gps: - attr[ATTR_LATITUDE] = self.gps[0], - attr[ATTR_LONGITUDE] = self.gps[1], + attr[ATTR_LATITUDE] = self.gps[0] + attr[ATTR_LONGITUDE] = self.gps[1] + + if self.battery: + attr[ATTR_BATTERY] = self.battery return attr @@ -321,11 +329,14 @@ class Device(Entity): """ If device should be hidden. """ return self.away_hide and self.state != STATE_HOME - def seen(self, host_name=None, location_name=None, gps=None): + def seen(self, host_name=None, location_name=None, gps=None, + gps_accuracy=0, battery=None): """ Mark the device as seen. """ self.last_seen = dt_util.utcnow() self.host_name = host_name self.location_name = location_name + self.gps_accuracy = gps_accuracy + self.battery = battery if gps is None: self.gps = None else: diff --git a/homeassistant/components/device_tracker/owntracks.py b/homeassistant/components/device_tracker/owntracks.py index 46a4c6b1e34..fdd1fc73b07 100644 --- a/homeassistant/components/device_tracker/owntracks.py +++ b/homeassistant/components/device_tracker/owntracks.py @@ -21,10 +21,21 @@ def setup_scanner(hass, config, see): def owntracks_location_update(topic, payload, qos): """ MQTT message received. """ + + # Docs on available data: + # http://owntracks.org/booklet/tech/json/#_typelocation + parts = topic.split('/') - data = json.loads(payload) + try: + data = json.loads(payload) + except ValueError: + # If invalid JSON + return + if data.get('_type') != 'location': + return dev_id = '{}_{}'.format(parts[1], parts[2]) - see(dev_id=dev_id, host_name=parts[1], gps=[data['lat'], data['lon']]) + see(dev_id=dev_id, host_name=parts[1], gps=(data['lat'], data['lon']), + gps_accuracy=data['acc'], battery=data['batt']) mqtt.subscribe(hass, LOCATION_TOPIC, owntracks_location_update, 1) diff --git a/homeassistant/util/location.py b/homeassistant/util/location.py index acb5deb675b..ade15131a8f 100644 --- a/homeassistant/util/location.py +++ b/homeassistant/util/location.py @@ -38,13 +38,11 @@ def distance(lon1, lat1, lon2, lat2): in decimal degrees on the earth using the Haversine algorithm. """ # convert decimal degrees to radians - lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2]) + lon1, lat1, lon2, lat2 = (radians(val) for val in (lon1, lat1, lon2, lat2)) - # haversine formula dlon = lon2 - lon1 dlat = lat2 - lat1 - a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 - c = 2 * asin(sqrt(a)) + angle = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 # Radius of earth in meters. radius = 6371000 - return c * radius + return 2 * radius * asin(sqrt(angle)) diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index 8b086e97c88..fb368bf863a 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -103,12 +103,12 @@ class TestComponentsDeviceTracker(unittest.TestCase): def test_reading_yaml_config(self): dev_id = 'test' device = device_tracker.Device( - self.hass, timedelta(seconds=180), True, dev_id, 'AB:CD:EF:GH:IJ', - 'Test name', 'http://test.picture', True) + self.hass, timedelta(seconds=180), 0, True, dev_id, + 'AB:CD:EF:GH:IJ', 'Test name', 'http://test.picture', True) device_tracker.update_config(self.yaml_devices, dev_id, device) self.assertTrue(device_tracker.setup(self.hass, {})) config = device_tracker.load_config(self.yaml_devices, self.hass, - device.consider_home)[0] + device.consider_home, 0)[0] self.assertEqual(device.dev_id, config.dev_id) self.assertEqual(device.track, config.track) self.assertEqual(device.mac, config.mac) @@ -126,7 +126,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): self.assertTrue(device_tracker.setup(self.hass, { device_tracker.DOMAIN: {CONF_PLATFORM: 'test'}})) config = device_tracker.load_config(self.yaml_devices, self.hass, - timedelta(seconds=0))[0] + timedelta(seconds=0), 0)[0] self.assertEqual('dev1', config.dev_id) self.assertEqual(True, config.track) @@ -176,7 +176,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): picture = 'http://placehold.it/200x200' device = device_tracker.Device( - self.hass, timedelta(seconds=180), True, dev_id, None, + self.hass, timedelta(seconds=180), 0, True, dev_id, None, friendly_name, picture, away_hide=True) device_tracker.update_config(self.yaml_devices, dev_id, device) @@ -191,7 +191,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): dev_id = 'test_entity' entity_id = device_tracker.ENTITY_ID_FORMAT.format(dev_id) device = device_tracker.Device( - self.hass, timedelta(seconds=180), True, dev_id, None, + self.hass, timedelta(seconds=180), 0, True, dev_id, None, away_hide=True) device_tracker.update_config(self.yaml_devices, dev_id, device) @@ -208,7 +208,7 @@ class TestComponentsDeviceTracker(unittest.TestCase): dev_id = 'test_entity' entity_id = device_tracker.ENTITY_ID_FORMAT.format(dev_id) device = device_tracker.Device( - self.hass, timedelta(seconds=180), True, dev_id, None, + self.hass, timedelta(seconds=180), 0, True, dev_id, None, away_hide=True) device_tracker.update_config(self.yaml_devices, dev_id, device)