mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Add template distance helper
This commit is contained in:
parent
6847dac582
commit
6ac54b20c7
@ -10,9 +10,10 @@ import logging
|
|||||||
import jinja2
|
import jinja2
|
||||||
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
||||||
|
|
||||||
from homeassistant.const import STATE_UNKNOWN
|
from homeassistant.const import STATE_UNKNOWN, ATTR_LATITUDE, ATTR_LONGITUDE
|
||||||
|
from homeassistant.core import State
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import TemplateError
|
||||||
import homeassistant.util.dt as dt_util
|
from homeassistant.util import convert, dt as dt_util, location
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_SENTINEL = object()
|
_SENTINEL = object()
|
||||||
@ -42,11 +43,14 @@ def render(hass, template, variables=None, **kwargs):
|
|||||||
if variables is not None:
|
if variables is not None:
|
||||||
kwargs.update(variables)
|
kwargs.update(variables)
|
||||||
|
|
||||||
|
location_helper = LocationHelpers(hass)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return ENV.from_string(template, {
|
return ENV.from_string(template, {
|
||||||
'states': AllStates(hass),
|
'distance': location_helper.distance,
|
||||||
'is_state': hass.states.is_state,
|
'is_state': hass.states.is_state,
|
||||||
'is_state_attr': hass.states.is_state_attr,
|
'is_state_attr': hass.states.is_state_attr,
|
||||||
|
'states': AllStates(hass),
|
||||||
'now': dt_util.now,
|
'now': dt_util.now,
|
||||||
'utcnow': dt_util.utcnow,
|
'utcnow': dt_util.utcnow,
|
||||||
}).render(kwargs).strip()
|
}).render(kwargs).strip()
|
||||||
@ -88,6 +92,61 @@ class DomainStates(object):
|
|||||||
key=lambda state: state.entity_id))
|
key=lambda state: state.entity_id))
|
||||||
|
|
||||||
|
|
||||||
|
class LocationHelpers(object):
|
||||||
|
"""Class to expose distance helpers to templates."""
|
||||||
|
|
||||||
|
def __init__(self, hass):
|
||||||
|
"""Initialize distance helpers."""
|
||||||
|
self._hass = hass
|
||||||
|
|
||||||
|
def distance(self, *args):
|
||||||
|
"""Calculate distance.
|
||||||
|
|
||||||
|
Will calculate distance from home to a point or between points.
|
||||||
|
Points can be passed in using state objects or lat/lng coordinates.
|
||||||
|
"""
|
||||||
|
locations = []
|
||||||
|
|
||||||
|
to_process = list(args)
|
||||||
|
|
||||||
|
while to_process:
|
||||||
|
value = to_process.pop(0)
|
||||||
|
|
||||||
|
if isinstance(value, State):
|
||||||
|
latitude = value.attributes.get(ATTR_LATITUDE)
|
||||||
|
longitude = value.attributes.get(ATTR_LONGITUDE)
|
||||||
|
|
||||||
|
if latitude is None or longitude is None:
|
||||||
|
_LOGGER.warning(
|
||||||
|
'Distance:State does not contains a location: %s',
|
||||||
|
value)
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# We expect this and next value to be lat&lng
|
||||||
|
if not to_process:
|
||||||
|
_LOGGER.warning(
|
||||||
|
'Distance:Expected latitude and longitude, got %s',
|
||||||
|
value)
|
||||||
|
return None
|
||||||
|
|
||||||
|
value_2 = to_process.pop(0)
|
||||||
|
latitude = convert(value, float)
|
||||||
|
longitude = convert(value_2, float)
|
||||||
|
|
||||||
|
if latitude is None or longitude is None:
|
||||||
|
_LOGGER.warning('Distance:Unable to process latitude and '
|
||||||
|
'longitude: %s, %s', value, value_2)
|
||||||
|
return None
|
||||||
|
|
||||||
|
locations.append((latitude, longitude))
|
||||||
|
|
||||||
|
if len(locations) == 1:
|
||||||
|
return self._hass.config.distance(*locations[0])
|
||||||
|
|
||||||
|
return location.distance(*locations[0] + locations[1])
|
||||||
|
|
||||||
|
|
||||||
def forgiving_round(value, precision=0):
|
def forgiving_round(value, precision=0):
|
||||||
""" Rounding method that accepts strings. """
|
""" Rounding method that accepts strings. """
|
||||||
try:
|
try:
|
||||||
|
@ -162,3 +162,84 @@ class TestUtilTemplate(unittest.TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
dt_util.utcnow().isoformat(),
|
dt_util.utcnow().isoformat(),
|
||||||
template.render(self.hass, '{{ utcnow().isoformat() }}'))
|
template.render(self.hass, '{{ utcnow().isoformat() }}'))
|
||||||
|
|
||||||
|
def test_distance_function_with_1_state(self):
|
||||||
|
self.hass.states.set('test.object', 'happy', {
|
||||||
|
'latitude': 32.87336,
|
||||||
|
'longitude': -117.22943,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass, '{{ distance(states.test.object) | round }}'))
|
||||||
|
|
||||||
|
def test_distance_function_with_2_states(self):
|
||||||
|
self.hass.states.set('test.object', 'happy', {
|
||||||
|
'latitude': 32.87336,
|
||||||
|
'longitude': -117.22943,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.hass.states.set('test.object_2', 'happy', {
|
||||||
|
'latitude': self.hass.config.latitude,
|
||||||
|
'longitude': self.hass.config.longitude,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance(states.test.object, states.test.object_2)'
|
||||||
|
'| round }}'))
|
||||||
|
|
||||||
|
def test_distance_function_with_1_coord(self):
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass, '{{ distance("32.87336", "-117.22943") | round }}'))
|
||||||
|
|
||||||
|
def test_distance_function_with_2_coords(self):
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance("32.87336", "-117.22943", %s, %s) | round }}'
|
||||||
|
% (self.hass.config.latitude, self.hass.config.longitude)))
|
||||||
|
|
||||||
|
def test_distance_function_with_1_state_1_coord(self):
|
||||||
|
self.hass.states.set('test.object_2', 'happy', {
|
||||||
|
'latitude': self.hass.config.latitude,
|
||||||
|
'longitude': self.hass.config.longitude,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance("32.87336", "-117.22943", states.test.object_2) '
|
||||||
|
'| round }}'))
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'187',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance(states.test.object_2, "32.87336", "-117.22943") '
|
||||||
|
'| round }}'))
|
||||||
|
|
||||||
|
def test_distance_function_return_None_if_invalid_state(self):
|
||||||
|
self.hass.states.set('test.object_2', 'happy', {
|
||||||
|
'latitude': 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
'None',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance(states.test.object_2) | round }}'))
|
||||||
|
|
||||||
|
def test_distance_function_return_None_if_invalid_coord(self):
|
||||||
|
self.assertEqual(
|
||||||
|
'None',
|
||||||
|
template.render(
|
||||||
|
self.hass,
|
||||||
|
'{{ distance("123", "abc") | round }}'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user