mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Merge pull request #1359 from balloob/template-helpers
New template helpers now, utcnow, distance, closest
This commit is contained in:
commit
c64da761f1
27
homeassistant/helpers/location.py
Normal file
27
homeassistant/helpers/location.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""Location helpers for Home Assistant."""
|
||||
|
||||
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
|
||||
from homeassistant.core import State
|
||||
from homeassistant.util import location as loc_util
|
||||
|
||||
|
||||
def has_location(state):
|
||||
"""Test if state contains a valid location."""
|
||||
return (isinstance(state, State) and
|
||||
isinstance(state.attributes.get(ATTR_LATITUDE), float) and
|
||||
isinstance(state.attributes.get(ATTR_LONGITUDE), float))
|
||||
|
||||
|
||||
def closest(latitude, longitude, states):
|
||||
"""Return closest state to point."""
|
||||
with_location = [state for state in states if has_location(state)]
|
||||
|
||||
if not with_location:
|
||||
return None
|
||||
|
||||
return min(
|
||||
with_location,
|
||||
key=lambda state: loc_util.distance(
|
||||
latitude, longitude, state.attributes.get(ATTR_LATITUDE),
|
||||
state.attributes.get(ATTR_LONGITUDE))
|
||||
)
|
@ -57,7 +57,7 @@ def convert(value, to_type, default=None):
|
||||
""" Converts value to to_type, returns default if fails. """
|
||||
try:
|
||||
return default if value is None else to_type(value)
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
# If value could not be converted
|
||||
return default
|
||||
|
||||
|
@ -10,8 +10,12 @@ import logging
|
||||
import jinja2
|
||||
from jinja2.sandbox import ImmutableSandboxedEnvironment
|
||||
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.components import group
|
||||
from homeassistant.const import STATE_UNKNOWN, ATTR_LATITUDE, ATTR_LONGITUDE
|
||||
from homeassistant.core import State
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import location as loc_helper
|
||||
from homeassistant.util import convert, dt as dt_util, location as loc_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_SENTINEL = object()
|
||||
@ -41,11 +45,18 @@ def render(hass, template, variables=None, **kwargs):
|
||||
if variables is not None:
|
||||
kwargs.update(variables)
|
||||
|
||||
location_methods = LocationMethods(hass)
|
||||
utcnow = dt_util.utcnow()
|
||||
|
||||
try:
|
||||
return ENV.from_string(template, {
|
||||
'states': AllStates(hass),
|
||||
'closest': location_methods.closest,
|
||||
'distance': location_methods.distance,
|
||||
'is_state': hass.states.is_state,
|
||||
'is_state_attr': hass.states.is_state_attr
|
||||
'is_state_attr': hass.states.is_state_attr,
|
||||
'now': dt_util.as_local(utcnow),
|
||||
'states': AllStates(hass),
|
||||
'utcnow': utcnow,
|
||||
}).render(kwargs).strip()
|
||||
except jinja2.TemplateError as err:
|
||||
raise TemplateError(err)
|
||||
@ -85,29 +96,155 @@ class DomainStates(object):
|
||||
key=lambda state: state.entity_id))
|
||||
|
||||
|
||||
class LocationMethods(object):
|
||||
"""Class to expose distance helpers to templates."""
|
||||
|
||||
def __init__(self, hass):
|
||||
"""Initialize distance helpers."""
|
||||
self._hass = hass
|
||||
|
||||
def closest(self, *args):
|
||||
"""Find closest entity.
|
||||
|
||||
Closest to home:
|
||||
closest(states)
|
||||
closest(states.device_tracker)
|
||||
closest('group.children')
|
||||
closest(states.group.children)
|
||||
|
||||
Closest to a point:
|
||||
closest(23.456, 23.456, 'group.children')
|
||||
closest('zone.school', 'group.children')
|
||||
closest(states.zone.school, 'group.children')
|
||||
"""
|
||||
|
||||
if len(args) == 1:
|
||||
latitude = self._hass.config.latitude
|
||||
longitude = self._hass.config.longitude
|
||||
entities = args[0]
|
||||
|
||||
elif len(args) == 2:
|
||||
point_state = self._resolve_state(args[0])
|
||||
|
||||
if point_state is None:
|
||||
_LOGGER.warning('Closest:Unable to find state %s', args[0])
|
||||
return None
|
||||
elif not loc_helper.has_location(point_state):
|
||||
_LOGGER.warning(
|
||||
'Closest:State does not contain valid location: %s',
|
||||
point_state)
|
||||
return None
|
||||
|
||||
latitude = point_state.attributes.get(ATTR_LATITUDE)
|
||||
longitude = point_state.attributes.get(ATTR_LONGITUDE)
|
||||
|
||||
entities = args[1]
|
||||
|
||||
else:
|
||||
latitude = convert(args[0], float)
|
||||
longitude = convert(args[1], float)
|
||||
|
||||
if latitude is None or longitude is None:
|
||||
_LOGGER.warning(
|
||||
'Closest:Received invalid coordinates: %s, %s',
|
||||
args[0], args[1])
|
||||
return None
|
||||
|
||||
entities = args[2]
|
||||
|
||||
if isinstance(entities, (AllStates, DomainStates)):
|
||||
states = list(entities)
|
||||
else:
|
||||
if isinstance(entities, State):
|
||||
gr_entity_id = entities.entity_id
|
||||
else:
|
||||
gr_entity_id = str(entities)
|
||||
|
||||
states = [self._hass.states.get(entity_id) for entity_id
|
||||
in group.expand_entity_ids(self._hass, [gr_entity_id])]
|
||||
|
||||
return loc_helper.closest(latitude, longitude, states)
|
||||
|
||||
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 loc_util.distance(*locations[0] + locations[1])
|
||||
|
||||
def _resolve_state(self, entity_id_or_state):
|
||||
"""Return state or entity_id if given."""
|
||||
if isinstance(entity_id_or_state, State):
|
||||
return entity_id_or_state
|
||||
elif isinstance(entity_id_or_state, str):
|
||||
return self._hass.states.get(entity_id_or_state)
|
||||
return None
|
||||
|
||||
|
||||
def forgiving_round(value, precision=0):
|
||||
""" Rounding method that accepts strings. """
|
||||
"""Rounding filter that accepts strings."""
|
||||
try:
|
||||
value = round(float(value), precision)
|
||||
return int(value) if precision == 0 else value
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
# If value can't be converted to float
|
||||
return value
|
||||
|
||||
|
||||
def multiply(value, amount):
|
||||
""" Converts to float and multiplies value. """
|
||||
"""Filter to convert value to float and multiply it."""
|
||||
try:
|
||||
return float(value) * amount
|
||||
except ValueError:
|
||||
except (ValueError, TypeError):
|
||||
# If value can't be converted to float
|
||||
return value
|
||||
|
||||
|
||||
class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
""" Home Assistant template environment. """
|
||||
"""Home Assistant template environment."""
|
||||
|
||||
def is_safe_callable(self, obj):
|
||||
"""Test if callback is safe."""
|
||||
return isinstance(obj, AllStates) or super().is_safe_callable(obj)
|
||||
|
||||
ENV = TemplateEnvironment()
|
||||
|
53
tests/helpers/test_location.py
Normal file
53
tests/helpers/test_location.py
Normal file
@ -0,0 +1,53 @@
|
||||
"""Tests Home Assistant location helpers."""
|
||||
# pylint: disable=too-many-public-methods
|
||||
import unittest
|
||||
|
||||
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
|
||||
from homeassistant.core import State
|
||||
from homeassistant.helpers import location
|
||||
|
||||
|
||||
class TestHelpersLocation(unittest.TestCase):
|
||||
def test_has_location_with_invalid_states(self):
|
||||
for state in (None, 1, "hello", object):
|
||||
self.assertFalse(location.has_location(state))
|
||||
|
||||
def test_has_location_with_states_with_invalid_locations(self):
|
||||
state = State('hello.world', 'invalid', {
|
||||
ATTR_LATITUDE: 'no number',
|
||||
ATTR_LONGITUDE: 123.12
|
||||
})
|
||||
self.assertFalse(location.has_location(state))
|
||||
|
||||
def test_has_location_with_states_with_valid_location(self):
|
||||
state = State('hello.world', 'invalid', {
|
||||
ATTR_LATITUDE: 123.12,
|
||||
ATTR_LONGITUDE: 123.12
|
||||
})
|
||||
self.assertTrue(location.has_location(state))
|
||||
|
||||
def test_closest_with_no_states_with_location(self):
|
||||
state = State('light.test', 'on')
|
||||
state2 = State('light.test', 'on', {
|
||||
ATTR_LATITUDE: 'invalid',
|
||||
ATTR_LONGITUDE: 123.45,
|
||||
})
|
||||
state3 = State('light.test', 'on', {
|
||||
ATTR_LONGITUDE: 123.45,
|
||||
})
|
||||
|
||||
self.assertIsNone(
|
||||
location.closest(123.45, 123.45, [state, state2, state3]))
|
||||
|
||||
def test_closest_returns_closest(self):
|
||||
state = State('light.test', 'on', {
|
||||
ATTR_LATITUDE: 124.45,
|
||||
ATTR_LONGITUDE: 124.45,
|
||||
})
|
||||
state2 = State('light.test', 'on', {
|
||||
ATTR_LATITUDE: 125.45,
|
||||
ATTR_LONGITUDE: 125.45,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
state, location.closest(123.45, 123.45, [state, state2]))
|
@ -53,6 +53,7 @@ class TestUtil(unittest.TestCase):
|
||||
self.assertEqual(True, util.convert("True", bool))
|
||||
self.assertEqual(1, util.convert("NOT A NUMBER", int, 1))
|
||||
self.assertEqual(1, util.convert(None, int, 1))
|
||||
self.assertEqual(1, util.convert(object, int, 1))
|
||||
|
||||
def test_ensure_unique_string(self):
|
||||
""" Test ensure_unique_string. """
|
||||
|
@ -6,8 +6,12 @@ Tests Home Assistant template util methods.
|
||||
"""
|
||||
# pylint: disable=too-many-public-methods
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components import group
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.util import template
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
@ -59,9 +63,6 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
self.hass,
|
||||
'{{ states.sensor.temperature.state | round(1) }}'))
|
||||
|
||||
def test_rounding_value2(self):
|
||||
self.hass.states.set('sensor.temperature', 12.78)
|
||||
|
||||
self.assertEqual(
|
||||
'128',
|
||||
template.render(
|
||||
@ -69,6 +70,34 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ states.sensor.temperature.state | multiply(10) | round }}'
|
||||
))
|
||||
|
||||
def test_rounding_value_get_original_value_on_error(self):
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ None | round }}'
|
||||
))
|
||||
|
||||
self.assertEqual(
|
||||
'no_number',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ "no_number" | round }}'
|
||||
))
|
||||
|
||||
def test_multiply(self):
|
||||
tests = {
|
||||
None: 'None',
|
||||
10: '100',
|
||||
'"abcd"': 'abcd'
|
||||
}
|
||||
|
||||
for inp, out in tests.items():
|
||||
self.assertEqual(
|
||||
out,
|
||||
template.render(self.hass,
|
||||
'{{ %s | multiply(10) | round }}' % inp))
|
||||
|
||||
def test_passing_vars_as_keywords(self):
|
||||
self.assertEqual(
|
||||
'127', template.render(self.hass, '{{ hello }}', hello=127))
|
||||
@ -143,3 +172,303 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
'unknown',
|
||||
template.render(self.hass, '{{ states("test.object2") }}'))
|
||||
|
||||
@patch('homeassistant.core.dt_util.utcnow', return_value=dt_util.utcnow())
|
||||
@patch('homeassistant.util.template.TemplateEnvironment.is_safe_callable',
|
||||
return_value=True)
|
||||
def test_now(self, mock_is_safe, mock_utcnow):
|
||||
self.assertEqual(
|
||||
dt_util.utcnow().isoformat(),
|
||||
template.render(self.hass, '{{ now.isoformat() }}'))
|
||||
|
||||
@patch('homeassistant.core.dt_util.utcnow', return_value=dt_util.utcnow())
|
||||
@patch('homeassistant.util.template.TemplateEnvironment.is_safe_callable',
|
||||
return_value=True)
|
||||
def test_utcnow(self, mock_is_safe, mock_utcnow):
|
||||
self.assertEqual(
|
||||
dt_util.utcnow().isoformat(),
|
||||
template.render(self.hass, '{{ utcnow.isoformat() }}'))
|
||||
|
||||
def test_utcnow_is_exactly_now(self):
|
||||
self.assertEqual(
|
||||
'True',
|
||||
template.render(self.hass, '{{ utcnow == now }}'))
|
||||
|
||||
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") }}'))
|
||||
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ distance("123") }}'))
|
||||
|
||||
self.hass.states.set('test.object_2', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ distance("123", states.test_object_2) }}'))
|
||||
|
||||
def test_closest_function_home_vs_domain(self):
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('not_test_domain.but_closer', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.object',
|
||||
template.render(self.hass,
|
||||
'{{ closest(states.test_domain).entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_all_states(self):
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('test_domain_2.and_closer', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain_2.and_closer',
|
||||
template.render(self.hass,
|
||||
'{{ closest(states).entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_group_entity_id(self):
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('not_in_group.but_closer', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
})
|
||||
|
||||
group.Group(self.hass, 'location group', ['test_domain.object'])
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.object',
|
||||
template.render(self.hass,
|
||||
'{{ closest("group.location_group").entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_group_state(self):
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('not_in_group.but_closer', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
})
|
||||
|
||||
group.Group(self.hass, 'location group', ['test_domain.object'])
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.object',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ closest(states.group.location_group).entity_id }}'))
|
||||
|
||||
def test_closest_function_to_coord(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('test_domain.closest_zone', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.2,
|
||||
'longitude': self.hass.config.longitude + 0.2,
|
||||
})
|
||||
|
||||
self.hass.states.set('zone.far_away', 'zoning', {
|
||||
'latitude': self.hass.config.latitude + 0.3,
|
||||
'longitude': self.hass.config.longitude + 0.3,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.closest_zone',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ closest("%s", %s, states.test_domain).entity_id }}'
|
||||
% (self.hass.config.latitude + 0.3,
|
||||
self.hass.config.longitude + 0.3))
|
||||
)
|
||||
|
||||
def test_closest_function_to_entity_id(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('test_domain.closest_zone', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.2,
|
||||
'longitude': self.hass.config.longitude + 0.2,
|
||||
})
|
||||
|
||||
self.hass.states.set('zone.far_away', 'zoning', {
|
||||
'latitude': self.hass.config.latitude + 0.3,
|
||||
'longitude': self.hass.config.longitude + 0.3,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.closest_zone',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ closest("zone.far_away", states.test_domain).entity_id }}')
|
||||
)
|
||||
|
||||
def test_closest_function_to_state(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.hass.states.set('test_domain.closest_zone', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.2,
|
||||
'longitude': self.hass.config.longitude + 0.2,
|
||||
})
|
||||
|
||||
self.hass.states.set('zone.far_away', 'zoning', {
|
||||
'latitude': self.hass.config.latitude + 0.3,
|
||||
'longitude': self.hass.config.longitude + 0.3,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'test_domain.closest_zone',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ closest(states.zone.far_away, '
|
||||
'states.test_domain).entity_id }}')
|
||||
)
|
||||
|
||||
def test_closest_function_invalid_state(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
for state in ('states.zone.non_existing', '"zone.non_existing"'):
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
self.hass, '{{ closest(%s, states) }}' % state))
|
||||
|
||||
def test_closest_function_state_with_invalid_location(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': 'invalid latitude',
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
self.hass,
|
||||
'{{ closest(states.test_domain.closest_home, '
|
||||
'states) }}'))
|
||||
|
||||
def test_closest_function_invalid_coordinates(self):
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(self.hass,
|
||||
'{{ closest("invalid", "coord", states) }}'))
|
||||
|
||||
def test_closest_function_no_location_states(self):
|
||||
self.assertEqual('None',
|
||||
template.render(self.hass, '{{ closest(states) }}'))
|
||||
|
Loading…
x
Reference in New Issue
Block a user