More robust geofence checking

This commit is contained in:
Paulus Schoutsen 2015-10-02 08:16:53 -07:00
parent 42b80868d4
commit 9bdfa89b7c
7 changed files with 44 additions and 36 deletions

View file

@ -36,17 +36,13 @@ def trigger(hass, config, action):
def zone_automation_listener(entity, from_s, to_s): def zone_automation_listener(entity, from_s, to_s):
""" Listens for state changes and calls action. """ """ Listens for state changes and calls action. """
if from_s and None in (from_s.attributes.get(ATTR_LATITUDE), if from_s and None in (from_s.attributes.get(ATTR_LATITUDE),
from_s.attributes.get(ATTR_LONGITUDE)): from_s.attributes.get(ATTR_LONGITUDE)) or \
None in (to_s.attributes.get(ATTR_LATITUDE),
to_s.attributes.get(ATTR_LONGITUDE)):
return return
if None in (to_s.attributes.get(ATTR_LATITUDE), from_match = _in_zone(hass, zone_entity_id, from_s) if from_s else None
to_s.attributes.get(ATTR_LONGITUDE)): to_match = _in_zone(hass, zone_entity_id, to_s)
return
from_zone = _in_zone(hass, from_s) if from_s else None
to_zone = _in_zone(hass, to_s)
from_match = from_zone and from_zone.entity_id == zone_entity_id
to_match = to_zone and to_zone.entity_id == zone_entity_id
if event == EVENT_ENTER and not from_match and to_match or \ if event == EVENT_ENTER and not from_match and to_match or \
event == EVENT_LEAVE and from_match and not to_match: event == EVENT_LEAVE and from_match and not to_match:
@ -71,22 +67,19 @@ def if_action(hass, config):
def if_in_zone(): def if_in_zone():
""" Test if condition. """ """ Test if condition. """
state = hass.states.get(entity_id) return _in_zone(hass, zone_entity_id, hass.states.get(entity_id))
if not state or None in (state.attributes.get(ATTR_LATITUDE),
state.attributes.get(ATTR_LONGITUDE)):
return
cur_zone = _in_zone(hass, state)
return cur_zone and cur_zone.entity_id == zone_entity_id
return if_in_zone return if_in_zone
def _in_zone(hass, state): def _in_zone(hass, zone_entity_id, state):
""" Check if state is in zone. """ """ Check if state is in zone. """
return zone.in_zone( if not state or None in (state.attributes.get(ATTR_LATITUDE),
hass, state.attributes.get(ATTR_LATITUDE), state.attributes.get(ATTR_LONGITUDE)):
return False
zone_state = hass.states.get(zone_entity_id)
return zone_state and zone.in_zone(
zone_state, state.attributes.get(ATTR_LATITUDE),
state.attributes.get(ATTR_LONGITUDE), state.attributes.get(ATTR_LONGITUDE),
state.attributes.get(ATTR_GPS_ACCURACY, 0)) state.attributes.get(ATTR_GPS_ACCURACY, 0))

View file

@ -364,7 +364,8 @@ class Device(Entity):
elif self.location_name: elif self.location_name:
self._state = self.location_name self._state = self.location_name
elif self.gps is not None: elif self.gps is not None:
zone_state = zone.in_zone(self.hass, self.gps[0], self.gps[1]) zone_state = zone.active_zone(self.hass, self.gps[0], self.gps[1],
self.gps_accuracy)
if zone_state is None: if zone_state is None:
self._state = STATE_NOT_HOME self._state = STATE_NOT_HOME
elif zone_state.entity_id == zone.ENTITY_ID_HOME: elif zone_state.entity_id == zone.ENTITY_ID_HOME:

View file

@ -1,2 +1,2 @@
""" DO NOT MODIFY. Auto-generated by build_frontend script """ """ DO NOT MODIFY. Auto-generated by build_frontend script """
VERSION = "8d460d7298e41397b4f94ef103b2a067" VERSION = "c4722afa376379bc4457d54bb9a38cee"

File diff suppressed because one or more lines are too long

@ -1 +1 @@
Subproject commit 4dba97292744a32146e35e36031dbca59eb62733 Subproject commit fa8d63f9ac5ef7b0533d800d2fdb8c0c92d56e12

View file

@ -44,8 +44,8 @@ ATTR_ICON = 'icon'
ICON_HOME = 'home' ICON_HOME = 'home'
def in_zone(hass, latitude, longitude, radius=0): def active_zone(hass, latitude, longitude, radius=0):
""" Find the zone for given latitude, longitude. """ """ Find the active zone for given latitude, longitude. """
# Sort entity IDs so that we are deterministic if equal distance to 2 zones # Sort entity IDs so that we are deterministic if equal distance to 2 zones
zones = (hass.states.get(entity_id) for entity_id zones = (hass.states.get(entity_id) for entity_id
in sorted(hass.states.entity_ids(DOMAIN))) in sorted(hass.states.entity_ids(DOMAIN)))
@ -54,18 +54,32 @@ def in_zone(hass, latitude, longitude, radius=0):
closest = None closest = None
for zone in zones: for zone in zones:
zone_dist = distance(latitude, longitude, zone_dist = distance(
zone.attributes[ATTR_LATITUDE], latitude, longitude,
zone.attributes[ATTR_LONGITUDE]) zone.attributes[ATTR_LATITUDE], zone.attributes[ATTR_LONGITUDE])
if zone_dist - radius < zone.attributes[ATTR_RADIUS] and \ within_zone = zone_dist - radius < zone.attributes[ATTR_RADIUS]
(closest is None or zone_dist < min_dist): closer_zone = closest is None or zone_dist < min_dist
smaller_zone = (zone_dist == min_dist and
zone.attributes[ATTR_RADIUS] <
closest.attributes[ATTR_RADIUS])
if within_zone and (closer_zone or smaller_zone):
min_dist = zone_dist min_dist = zone_dist
closest = zone closest = zone
return closest return closest
def in_zone(zone, latitude, longitude, radius=0):
""" Test if given latitude, longitude is in given zone. """
zone_dist = distance(
latitude, longitude,
zone.attributes[ATTR_LATITUDE], zone.attributes[ATTR_LONGITUDE])
return zone_dist - radius < zone.attributes[ATTR_RADIUS]
def setup(hass, config): def setup(hass, config):
""" Setup zone. """ """ Setup zone. """
entities = set() entities = set()

View file

@ -31,6 +31,6 @@ def detect_location_info():
return LocationInfo(**data) return LocationInfo(**data)
def distance(lon1, lat1, lon2, lat2): def distance(lat1, lon1, lat2, lon2):
""" Calculate the distance in meters between two points. """ """ Calculate the distance in meters between two points. """
return vincenty((lon1, lat1), (lon2, lat2)) * 1000 return vincenty((lat1, lon1), (lat2, lon2)) * 1000