mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Move details to docs and update docstrings/comments
This commit is contained in:
parent
757946293e
commit
e311f89056
@ -1,55 +1,12 @@
|
|||||||
"""
|
"""
|
||||||
custom_components.proximity
|
homeassistant.components.proximity
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Component to monitor the proximity of devices to a particular zone and the
|
Component to monitor the proximity of devices to a particular zone and the
|
||||||
direction of travel. The result is an entity created in HA which maintains
|
direction of travel.
|
||||||
the proximity data
|
|
||||||
|
|
||||||
This component is useful to reduce the number of automation rules required
|
For more details about this component, please refer to the documentation at
|
||||||
when wanting to perform automations based on locations outside a particular
|
https://home-assistant.io/components/proximity/
|
||||||
zone. The standard HA zone and state based triggers allow similar control
|
|
||||||
but the number of rules grows exponentially when factors such as direction
|
|
||||||
of travel need to be taken into account. Some examples of its use include:
|
|
||||||
- Increase thermostat temperature as you near home
|
|
||||||
- Decrease temperature the further away from home you travel
|
|
||||||
|
|
||||||
The Proximity entity which is created has the following values:
|
|
||||||
state = distance from the monitored zone (in km)
|
|
||||||
dir_of_travel = direction of the closest device to the monitoed zone. Values
|
|
||||||
are:
|
|
||||||
'not set'
|
|
||||||
'arrived'
|
|
||||||
'towards'
|
|
||||||
'away_from'
|
|
||||||
'unknown'
|
|
||||||
'stationary'
|
|
||||||
dist_to_zone = distance from the monitored zone (in km)
|
|
||||||
|
|
||||||
Use configuration.yaml to enable the user to easily tune a number of settings:
|
|
||||||
- Zone: the zone to which this component is measuring the distance to. Default
|
|
||||||
is the home zone
|
|
||||||
- Ignored Zones: where proximity is not calculated for a device (either the
|
|
||||||
device being monitored or ones being compared (e.g. work or school)
|
|
||||||
- Devices: a list of devices to compare location against to check closeness to
|
|
||||||
the configured zone
|
|
||||||
- Tolerance: the tolerance used to calculate the direction of travel in metres
|
|
||||||
(to filter out small GPS co-ordinate changes
|
|
||||||
|
|
||||||
Logging levels debug, info and error are in use
|
|
||||||
|
|
||||||
Example configuration.yaml entry:
|
|
||||||
proximity:
|
|
||||||
zone: home
|
|
||||||
ignored_zones:
|
|
||||||
- twork
|
|
||||||
- elschool
|
|
||||||
devices:
|
|
||||||
- device_tracker.nwaring_nickmobile
|
|
||||||
- device_tracker.eleanorsiphone
|
|
||||||
- device_tracker.tsiphone
|
|
||||||
tolerance: 50
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from homeassistant.helpers.event import track_state_change
|
from homeassistant.helpers.event import track_state_change
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
@ -57,33 +14,31 @@ from homeassistant.util.location import distance
|
|||||||
|
|
||||||
DEPENDENCIES = ['zone', 'device_tracker']
|
DEPENDENCIES = ['zone', 'device_tracker']
|
||||||
|
|
||||||
# domain for the component
|
|
||||||
DOMAIN = 'proximity'
|
DOMAIN = 'proximity'
|
||||||
|
|
||||||
# default tolerance
|
# Default tolerance
|
||||||
DEFAULT_TOLERANCE = 1
|
DEFAULT_TOLERANCE = 1
|
||||||
|
|
||||||
# default zone
|
# Default zone
|
||||||
DEFAULT_PROXIMITY_ZONE = 'home'
|
DEFAULT_PROXIMITY_ZONE = 'home'
|
||||||
|
|
||||||
# entity attributes
|
# Entity attributes
|
||||||
ATTR_DIST_FROM = 'dist_to_zone'
|
ATTR_DIST_FROM = 'dist_to_zone'
|
||||||
ATTR_DIR_OF_TRAVEL = 'dir_of_travel'
|
ATTR_DIR_OF_TRAVEL = 'dir_of_travel'
|
||||||
ATTR_NEAREST = 'nearest'
|
ATTR_NEAREST = 'nearest'
|
||||||
ATTR_FRIENDLY_NAME = 'friendly_name'
|
ATTR_FRIENDLY_NAME = 'friendly_name'
|
||||||
|
|
||||||
# Shortcut for the logger
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config): # pylint: disable=too-many-locals,too-many-statements
|
def setup(hass, config): # pylint: disable=too-many-locals,too-many-statements
|
||||||
""" get the zones and offsets from configuration.yaml"""
|
""" Get the zones and offsets from configuration.yaml. """
|
||||||
ignored_zones = []
|
ignored_zones = []
|
||||||
if 'ignored_zones' in config[DOMAIN]:
|
if 'ignored_zones' in config[DOMAIN]:
|
||||||
for variable in config[DOMAIN]['ignored_zones']:
|
for variable in config[DOMAIN]['ignored_zones']:
|
||||||
ignored_zones.append(variable)
|
ignored_zones.append(variable)
|
||||||
|
|
||||||
# get the devices from configuration.yaml
|
# Get the devices from configuration.yaml
|
||||||
if 'devices' not in config[DOMAIN]:
|
if 'devices' not in config[DOMAIN]:
|
||||||
_LOGGER.error('devices not found in config')
|
_LOGGER.error('devices not found in config')
|
||||||
return False
|
return False
|
||||||
@ -92,10 +47,10 @@ def setup(hass, config): # pylint: disable=too-many-locals,too-many-statements
|
|||||||
for variable in config[DOMAIN]['devices']:
|
for variable in config[DOMAIN]['devices']:
|
||||||
proximity_devices.append(variable)
|
proximity_devices.append(variable)
|
||||||
|
|
||||||
# get the direction of travel tolerance from configuration.yaml
|
# Gget the direction of travel tolerance from configuration.yaml
|
||||||
tolerance = config[DOMAIN].get('tolerance', DEFAULT_TOLERANCE)
|
tolerance = config[DOMAIN].get('tolerance', DEFAULT_TOLERANCE)
|
||||||
|
|
||||||
# get the zone to monitor proximity to from configuration.yaml
|
# Get the zone to monitor proximity to from configuration.yaml
|
||||||
proximity_zone = config[DOMAIN].get('zone', DEFAULT_PROXIMITY_ZONE)
|
proximity_zone = config[DOMAIN].get('zone', DEFAULT_PROXIMITY_ZONE)
|
||||||
|
|
||||||
entity_id = DOMAIN + '.' + proximity_zone
|
entity_id = DOMAIN + '.' + proximity_zone
|
||||||
@ -116,7 +71,7 @@ def setup(hass, config): # pylint: disable=too-many-locals,too-many-statements
|
|||||||
|
|
||||||
proximity.update_ha_state()
|
proximity.update_ha_state()
|
||||||
|
|
||||||
# main command to monitor proximity of devices
|
# Main command to monitor proximity of devices
|
||||||
track_state_change(hass, proximity_devices,
|
track_state_change(hass, proximity_devices,
|
||||||
proximity.check_proximity_state_change)
|
proximity.check_proximity_state_change)
|
||||||
|
|
||||||
@ -125,7 +80,7 @@ def setup(hass, config): # pylint: disable=too-many-locals,too-many-statements
|
|||||||
|
|
||||||
|
|
||||||
class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
||||||
""" Represents a Proximity in Home Assistant. """
|
""" Represents a Proximity. """
|
||||||
def __init__(self, hass, zone_friendly_name, dist_to, dir_of_travel,
|
def __init__(self, hass, zone_friendly_name, dist_to, dir_of_travel,
|
||||||
nearest, ignored_zones, proximity_devices, tolerance,
|
nearest, ignored_zones, proximity_devices, tolerance,
|
||||||
proximity_zone):
|
proximity_zone):
|
||||||
@ -142,6 +97,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
|
""" Returns the state. """
|
||||||
return self.dist_to
|
return self.dist_to
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -151,6 +107,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def state_attributes(self):
|
def state_attributes(self):
|
||||||
|
""" Returns the state attributes. """
|
||||||
return {
|
return {
|
||||||
ATTR_DIR_OF_TRAVEL: self.dir_of_travel,
|
ATTR_DIR_OF_TRAVEL: self.dir_of_travel,
|
||||||
ATTR_NEAREST: self.nearest,
|
ATTR_NEAREST: self.nearest,
|
||||||
@ -159,7 +116,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
def check_proximity_state_change(self, entity, old_state, new_state):
|
def check_proximity_state_change(self, entity, old_state, new_state):
|
||||||
# pylint: disable=too-many-branches,too-many-statements,too-many-locals
|
# pylint: disable=too-many-branches,too-many-statements,too-many-locals
|
||||||
""" Function to perform the proximity checking """
|
""" Function to perform the proximity checking. """
|
||||||
entity_name = new_state.name
|
entity_name = new_state.name
|
||||||
devices_to_calculate = False
|
devices_to_calculate = False
|
||||||
devices_in_zone = ''
|
devices_in_zone = ''
|
||||||
@ -168,21 +125,21 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
proximity_latitude = zone_state.attributes.get('latitude')
|
proximity_latitude = zone_state.attributes.get('latitude')
|
||||||
proximity_longitude = zone_state.attributes.get('longitude')
|
proximity_longitude = zone_state.attributes.get('longitude')
|
||||||
|
|
||||||
# check for devices in the monitored zone
|
# Check for devices in the monitored zone
|
||||||
for device in self.proximity_devices:
|
for device in self.proximity_devices:
|
||||||
device_state = self.hass.states.get(device)
|
device_state = self.hass.states.get(device)
|
||||||
|
|
||||||
if device_state.state not in self.ignored_zones:
|
if device_state.state not in self.ignored_zones:
|
||||||
devices_to_calculate = True
|
devices_to_calculate = True
|
||||||
|
|
||||||
# check the location of all devices
|
# Check the location of all devices
|
||||||
if (device_state.state).lower() == (self.friendly_name).lower():
|
if (device_state.state).lower() == (self.friendly_name).lower():
|
||||||
device_friendly = device_state.name
|
device_friendly = device_state.name
|
||||||
if devices_in_zone != '':
|
if devices_in_zone != '':
|
||||||
devices_in_zone = devices_in_zone + ', '
|
devices_in_zone = devices_in_zone + ', '
|
||||||
devices_in_zone = devices_in_zone + device_friendly
|
devices_in_zone = devices_in_zone + device_friendly
|
||||||
|
|
||||||
# no-one to track so reset the entity
|
# No-one to track so reset the entity
|
||||||
if not devices_to_calculate:
|
if not devices_to_calculate:
|
||||||
self.dist_to = 'not set'
|
self.dist_to = 'not set'
|
||||||
self.dir_of_travel = 'not set'
|
self.dir_of_travel = 'not set'
|
||||||
@ -190,7 +147,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
# at least one device is in the monitored zone so update the entity
|
# At least one device is in the monitored zone so update the entity
|
||||||
if devices_in_zone != '':
|
if devices_in_zone != '':
|
||||||
self.dist_to = 0
|
self.dist_to = 0
|
||||||
self.dir_of_travel = 'arrived'
|
self.dir_of_travel = 'arrived'
|
||||||
@ -198,32 +155,32 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
# we can't check proximity because latitude and longitude don't exist
|
# We can't check proximity because latitude and longitude don't exist
|
||||||
if 'latitude' not in new_state.attributes:
|
if 'latitude' not in new_state.attributes:
|
||||||
return
|
return
|
||||||
|
|
||||||
# collect distances to the zone for all devices
|
# Collect distances to the zone for all devices
|
||||||
distances_to_zone = {}
|
distances_to_zone = {}
|
||||||
for device in self.proximity_devices:
|
for device in self.proximity_devices:
|
||||||
# ignore devices in an ignored zone
|
# Ignore devices in an ignored zone
|
||||||
device_state = self.hass.states.get(device)
|
device_state = self.hass.states.get(device)
|
||||||
if device_state.state in self.ignored_zones:
|
if device_state.state in self.ignored_zones:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ignore devices if proximity cannot be calculated
|
# Ignore devices if proximity cannot be calculated
|
||||||
if 'latitude' not in device_state.attributes:
|
if 'latitude' not in device_state.attributes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# calculate the distance to the proximity zone
|
# Calculate the distance to the proximity zone
|
||||||
dist_to_zone = distance(proximity_latitude,
|
dist_to_zone = distance(proximity_latitude,
|
||||||
proximity_longitude,
|
proximity_longitude,
|
||||||
device_state.attributes['latitude'],
|
device_state.attributes['latitude'],
|
||||||
device_state.attributes['longitude'])
|
device_state.attributes['longitude'])
|
||||||
|
|
||||||
# add the device and distance to a dictionary
|
# Add the device and distance to a dictionary
|
||||||
distances_to_zone[device] = round(dist_to_zone / 1000, 1)
|
distances_to_zone[device] = round(dist_to_zone / 1000, 1)
|
||||||
|
|
||||||
# loop through each of the distances collected and work out the closest
|
# Loop through each of the distances collected and work out the closest
|
||||||
closest_device = ''
|
closest_device = ''
|
||||||
dist_to_zone = 1000000
|
dist_to_zone = 1000000
|
||||||
|
|
||||||
@ -232,7 +189,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
closest_device = device
|
closest_device = device
|
||||||
dist_to_zone = distances_to_zone[device]
|
dist_to_zone = distances_to_zone[device]
|
||||||
|
|
||||||
# if the closest device is one of the other devices
|
# If the closest device is one of the other devices
|
||||||
if closest_device != entity:
|
if closest_device != entity:
|
||||||
self.dist_to = round(distances_to_zone[closest_device])
|
self.dist_to = round(distances_to_zone[closest_device])
|
||||||
self.dir_of_travel = 'unknown'
|
self.dir_of_travel = 'unknown'
|
||||||
@ -241,7 +198,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
# stop if we cannot calculate the direction of travel (i.e. we don't
|
# Stop if we cannot calculate the direction of travel (i.e. we don't
|
||||||
# have a previous state and a current LAT and LONG)
|
# have a previous state and a current LAT and LONG)
|
||||||
if old_state is None or 'latitude' not in old_state.attributes:
|
if old_state is None or 'latitude' not in old_state.attributes:
|
||||||
self.dist_to = round(distances_to_zone[entity])
|
self.dist_to = round(distances_to_zone[entity])
|
||||||
@ -250,10 +207,10 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
self.update_ha_state()
|
self.update_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
# reset the variables
|
# Reset the variables
|
||||||
distance_travelled = 0
|
distance_travelled = 0
|
||||||
|
|
||||||
# calculate the distance travelled
|
# Calculate the distance travelled
|
||||||
old_distance = distance(proximity_latitude, proximity_longitude,
|
old_distance = distance(proximity_latitude, proximity_longitude,
|
||||||
old_state.attributes['latitude'],
|
old_state.attributes['latitude'],
|
||||||
old_state.attributes['longitude'])
|
old_state.attributes['longitude'])
|
||||||
@ -262,7 +219,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
new_state.attributes['longitude'])
|
new_state.attributes['longitude'])
|
||||||
distance_travelled = round(new_distance - old_distance, 1)
|
distance_travelled = round(new_distance - old_distance, 1)
|
||||||
|
|
||||||
# check for tolerance
|
# Check for tolerance
|
||||||
if distance_travelled < self.tolerance * -1:
|
if distance_travelled < self.tolerance * -1:
|
||||||
direction_of_travel = 'towards'
|
direction_of_travel = 'towards'
|
||||||
elif distance_travelled > self.tolerance:
|
elif distance_travelled > self.tolerance:
|
||||||
@ -270,7 +227,7 @@ class Proximity(Entity): # pylint: disable=too-many-instance-attributes
|
|||||||
else:
|
else:
|
||||||
direction_of_travel = 'stationary'
|
direction_of_travel = 'stationary'
|
||||||
|
|
||||||
# update the proximity entity
|
# Update the proximity entity
|
||||||
self.dist_to = round(dist_to_zone)
|
self.dist_to = round(dist_to_zone)
|
||||||
self.dir_of_travel = direction_of_travel
|
self.dir_of_travel = direction_of_travel
|
||||||
self.nearest = entity_name
|
self.nearest = entity_name
|
||||||
|
Loading…
x
Reference in New Issue
Block a user