mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Pre-compile templates (#3515)
* Pre-compile templates * Compile templates in numeric_state condition
This commit is contained in:
parent
b3d67a7ed9
commit
0c0feda834
@ -12,7 +12,7 @@ from homeassistant.const import (
|
||||
CONF_VALUE_TEMPLATE, CONF_PLATFORM, CONF_ENTITY_ID,
|
||||
CONF_BELOW, CONF_ABOVE)
|
||||
from homeassistant.helpers.event import track_state_change
|
||||
from homeassistant.helpers import condition, config_validation as cv
|
||||
from homeassistant.helpers import condition, config_validation as cv, template
|
||||
|
||||
TRIGGER_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Required(CONF_PLATFORM): 'numeric_state',
|
||||
@ -31,6 +31,8 @@ def trigger(hass, config, action):
|
||||
below = config.get(CONF_BELOW)
|
||||
above = config.get(CONF_ABOVE)
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def state_automation_listener(entity, from_s, to_s):
|
||||
|
@ -10,7 +10,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_VALUE_TEMPLATE, CONF_PLATFORM, MATCH_ALL)
|
||||
from homeassistant.helpers import condition
|
||||
from homeassistant.helpers import condition, template
|
||||
from homeassistant.helpers.event import track_state_change
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
@ -25,7 +25,8 @@ TRIGGER_SCHEMA = IF_ACTION_SCHEMA = vol.Schema({
|
||||
|
||||
def trigger(hass, config, action):
|
||||
"""Listen for state changes based on configuration."""
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
value_template = template.compile_template(
|
||||
hass, config.get(CONF_VALUE_TEMPLATE))
|
||||
|
||||
# Local variable to keep track of if the action has already been triggered
|
||||
already_triggered = False
|
||||
|
@ -46,6 +46,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
sensor_class = config.get(CONF_SENSOR_CLASS)
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
data = CommandSensorData(command)
|
||||
|
||||
add_devices([CommandBinarySensor(
|
||||
|
@ -37,6 +37,11 @@ PLATFORM_SCHEMA = mqtt.MQTT_RO_PLATFORM_SCHEMA.extend({
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the MQTT binary sensor."""
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
add_devices([MqttBinarySensor(
|
||||
hass,
|
||||
config.get(CONF_NAME),
|
||||
@ -45,7 +50,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
config.get(CONF_QOS),
|
||||
config.get(CONF_PAYLOAD_ON),
|
||||
config.get(CONF_PAYLOAD_OFF),
|
||||
config.get(CONF_VALUE_TEMPLATE)
|
||||
value_template
|
||||
)])
|
||||
|
||||
|
||||
|
@ -45,6 +45,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
sensor_class = config.get(CONF_SENSOR_CLASS)
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
rest = RestData(method, resource, payload, verify_ssl)
|
||||
rest.update()
|
||||
|
||||
|
@ -73,7 +73,7 @@ class BinarySensorTemplate(BinarySensorDevice):
|
||||
hass=hass)
|
||||
self._name = friendly_name
|
||||
self._sensor_class = sensor_class
|
||||
self._template = value_template
|
||||
self._template = template.compile_template(hass, value_template)
|
||||
self._state = None
|
||||
|
||||
self.update()
|
||||
|
@ -42,7 +42,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the template sensors."""
|
||||
"""Setup the trend sensors."""
|
||||
sensors = []
|
||||
|
||||
for device, device_config in config[CONF_SENSORS].items():
|
||||
|
@ -38,18 +38,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup a generic IP Camera."""
|
||||
add_devices([GenericCamera(config)])
|
||||
add_devices([GenericCamera(hass, config)])
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class GenericCamera(Camera):
|
||||
"""A generic implementation of an IP camera."""
|
||||
|
||||
def __init__(self, device_info):
|
||||
def __init__(self, hass, device_info):
|
||||
"""Initialize a generic camera."""
|
||||
super().__init__()
|
||||
self.hass = hass
|
||||
self._name = device_info.get(CONF_NAME)
|
||||
self._still_image_url = device_info[CONF_STILL_IMAGE_URL]
|
||||
self._still_image_url = template.compile_template(
|
||||
hass, device_info[CONF_STILL_IMAGE_URL])
|
||||
self._limit_refetch = device_info[CONF_LIMIT_REFETCH_TO_URL_CHANGE]
|
||||
|
||||
username = device_info.get(CONF_USERNAME)
|
||||
|
@ -24,7 +24,7 @@ COVER_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_COMMAND_STATE): cv.string,
|
||||
vol.Optional(CONF_COMMAND_STOP, default='true'): cv.string,
|
||||
vol.Optional(CONF_FRIENDLY_NAME): cv.string,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE, default='{{ value }}'): cv.template,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||
})
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
@ -38,6 +38,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
covers = []
|
||||
|
||||
for device_name, device_config in devices.items():
|
||||
value_template = device_config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
covers.append(
|
||||
CommandCover(
|
||||
hass,
|
||||
@ -46,7 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
device_config.get(CONF_COMMAND_CLOSE),
|
||||
device_config.get(CONF_COMMAND_STOP),
|
||||
device_config.get(CONF_COMMAND_STATE),
|
||||
device_config.get(CONF_VALUE_TEMPLATE),
|
||||
value_template,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -44,11 +44,17 @@ PLATFORM_SCHEMA = mqtt.MQTT_RW_PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_STATE_CLOSED, default=STATE_CLOSED): cv.string,
|
||||
vol.Optional(CONF_OPTIMISTIC, default=DEFAULT_OPTIMISTIC): cv.boolean,
|
||||
vol.Optional(CONF_RETAIN, default=DEFAULT_RETAIN): cv.boolean,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the MQTT Cover."""
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
if value_template is not None:
|
||||
value_template = template.compile_template(hass, value_template)
|
||||
|
||||
add_devices([MqttCover(
|
||||
hass,
|
||||
config.get(CONF_NAME),
|
||||
@ -62,7 +68,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
config.get(CONF_PAYLOAD_CLOSE),
|
||||
config.get(CONF_PAYLOAD_STOP),
|
||||
config.get(CONF_OPTIMISTIC),
|
||||
config.get(CONF_VALUE_TEMPLATE)
|
||||
value_template,
|
||||
)])
|
||||
|
||||
|
||||
|
@ -71,7 +71,7 @@ class SensorTemplate(Entity):
|
||||
hass=hass)
|
||||
self._name = friendly_name
|
||||
self._unit_of_measurement = unit_of_measurement
|
||||
self._template = state_template
|
||||
self._template = template.compile_template(hass, state_template)
|
||||
self._state = None
|
||||
|
||||
self.update()
|
||||
|
@ -79,7 +79,7 @@ class SwitchTemplate(SwitchDevice):
|
||||
self.entity_id = generate_entity_id(ENTITY_ID_FORMAT, device_id,
|
||||
hass=hass)
|
||||
self._name = friendly_name
|
||||
self._template = state_template
|
||||
self._template = template.compile_template(hass, state_template)
|
||||
self._on_script = Script(hass, on_action)
|
||||
self._off_script = Script(hass, off_action)
|
||||
self._state = False
|
||||
|
@ -16,7 +16,7 @@ from homeassistant.const import (
|
||||
CONF_BELOW, CONF_ABOVE)
|
||||
from homeassistant.exceptions import TemplateError, HomeAssistantError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.template import render
|
||||
from homeassistant.helpers.template import render, compile_template
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
FROM_CONFIG_FORMAT = '{}_from_config'
|
||||
@ -125,9 +125,18 @@ def numeric_state_from_config(config, config_validation=True):
|
||||
above = config.get(CONF_ABOVE)
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
cache = {}
|
||||
|
||||
def if_numeric_state(hass, variables=None):
|
||||
"""Test numeric state condition."""
|
||||
return numeric_state(hass, entity_id, below, above, value_template,
|
||||
if value_template is None:
|
||||
tmpl = None
|
||||
elif hass in cache:
|
||||
tmpl = cache[hass]
|
||||
else:
|
||||
cache[hass] = tmpl = compile_template(hass, value_template)
|
||||
|
||||
return numeric_state(hass, entity_id, below, above, tmpl,
|
||||
variables)
|
||||
|
||||
return if_numeric_state
|
||||
@ -222,9 +231,16 @@ def template_from_config(config, config_validation=True):
|
||||
config = cv.TEMPLATE_CONDITION_SCHEMA(config)
|
||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||
|
||||
cache = {}
|
||||
|
||||
def template_if(hass, variables=None):
|
||||
"""Validate template based if-condition."""
|
||||
return template(hass, value_template, variables)
|
||||
if hass in cache:
|
||||
tmpl = cache[hass]
|
||||
else:
|
||||
cache[hass] = tmpl = compile_template(hass, value_template)
|
||||
|
||||
return template(hass, tmpl, variables)
|
||||
|
||||
return template_if
|
||||
|
||||
|
@ -18,6 +18,24 @@ _SENTINEL = object()
|
||||
DATE_STR_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
|
||||
def compile_template(hass, template):
|
||||
"""Compile a template."""
|
||||
location_methods = LocationMethods(hass)
|
||||
|
||||
return ENV.from_string(template, {
|
||||
'closest': location_methods.closest,
|
||||
'distance': location_methods.distance,
|
||||
'float': forgiving_float,
|
||||
'is_state': hass.states.is_state,
|
||||
'is_state_attr': hass.states.is_state_attr,
|
||||
'now': dt_util.now,
|
||||
'states': AllStates(hass),
|
||||
'utcnow': dt_util.utcnow,
|
||||
'as_timestamp': dt_util.as_timestamp,
|
||||
'relative_time': dt_util.get_age
|
||||
})
|
||||
|
||||
|
||||
def render_with_possible_json_value(hass, template, value,
|
||||
error_value=_SENTINEL):
|
||||
"""Render template with value exposed.
|
||||
@ -44,22 +62,11 @@ 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, {
|
||||
'closest': location_methods.closest,
|
||||
'distance': location_methods.distance,
|
||||
'float': forgiving_float,
|
||||
'is_state': hass.states.is_state,
|
||||
'is_state_attr': hass.states.is_state_attr,
|
||||
'now': dt_util.as_local(utcnow),
|
||||
'states': AllStates(hass),
|
||||
'utcnow': utcnow,
|
||||
'as_timestamp': dt_util.as_timestamp,
|
||||
'relative_time': dt_util.get_age
|
||||
}).render(kwargs).strip()
|
||||
if not isinstance(template, jinja2.Template):
|
||||
template = compile_template(hass, template)
|
||||
|
||||
return template.render(kwargs).strip()
|
||||
except jinja2.TemplateError as err:
|
||||
raise TemplateError(err)
|
||||
|
||||
|
@ -33,14 +33,14 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
self.hass.stop()
|
||||
|
||||
def test_referring_states_by_entity_id(self):
|
||||
"""."""
|
||||
"""Test referring states by entity id."""
|
||||
self.hass.states.set('test.object', 'happy')
|
||||
self.assertEqual(
|
||||
'happy',
|
||||
template.render(self.hass, '{{ states.test.object.state }}'))
|
||||
|
||||
def test_iterating_all_states(self):
|
||||
"""."""
|
||||
"""Test iterating all states."""
|
||||
self.hass.states.set('test.object', 'happy')
|
||||
self.hass.states.set('sensor.temperature', 10)
|
||||
|
||||
@ -51,7 +51,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{% for state in states %}{{ state.state }}{% endfor %}'))
|
||||
|
||||
def test_iterating_domain_states(self):
|
||||
"""."""
|
||||
"""Test iterating domain states."""
|
||||
self.hass.states.set('test.object', 'happy')
|
||||
self.hass.states.set('sensor.back_door', 'open')
|
||||
self.hass.states.set('sensor.temperature', 10)
|
||||
@ -65,7 +65,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
"""))
|
||||
|
||||
def test_float(self):
|
||||
"""."""
|
||||
"""Test float."""
|
||||
self.hass.states.set('sensor.temperature', '12')
|
||||
|
||||
self.assertEqual(
|
||||
@ -81,7 +81,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ float(states.sensor.temperature.state) > 11 }}'))
|
||||
|
||||
def test_rounding_value(self):
|
||||
"""."""
|
||||
"""Test rounding value."""
|
||||
self.hass.states.set('sensor.temperature', 12.78)
|
||||
|
||||
self.assertEqual(
|
||||
@ -98,7 +98,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
))
|
||||
|
||||
def test_rounding_value_get_original_value_on_error(self):
|
||||
"""."""
|
||||
"""Test rounding value get original value on error."""
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
@ -114,7 +114,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
))
|
||||
|
||||
def test_multiply(self):
|
||||
"""."""
|
||||
"""Test multiply."""
|
||||
tests = {
|
||||
None: 'None',
|
||||
10: '100',
|
||||
@ -180,50 +180,50 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ %s | timestamp_utc }}' % inp))
|
||||
|
||||
def test_passing_vars_as_keywords(self):
|
||||
"""."""
|
||||
"""Test passing variables as keywords."""
|
||||
self.assertEqual(
|
||||
'127', template.render(self.hass, '{{ hello }}', hello=127))
|
||||
|
||||
def test_passing_vars_as_vars(self):
|
||||
"""."""
|
||||
"""Test passing variables as variables."""
|
||||
self.assertEqual(
|
||||
'127', template.render(self.hass, '{{ hello }}', {'hello': 127}))
|
||||
|
||||
def test_render_with_possible_json_value_with_valid_json(self):
|
||||
"""."""
|
||||
"""Render with possible JSON value with valid JSON."""
|
||||
self.assertEqual(
|
||||
'world',
|
||||
template.render_with_possible_json_value(
|
||||
self.hass, '{{ value_json.hello }}', '{"hello": "world"}'))
|
||||
|
||||
def test_render_with_possible_json_value_with_invalid_json(self):
|
||||
"""."""
|
||||
"""Render with possible JSON value with invalid JSON."""
|
||||
self.assertEqual(
|
||||
'',
|
||||
template.render_with_possible_json_value(
|
||||
self.hass, '{{ value_json }}', '{ I AM NOT JSON }'))
|
||||
|
||||
def test_render_with_possible_json_value_with_template_error(self):
|
||||
"""."""
|
||||
"""Render with possible JSON value with template error."""
|
||||
self.assertEqual(
|
||||
'hello',
|
||||
template.render_with_possible_json_value(
|
||||
self.hass, '{{ value_json', 'hello'))
|
||||
|
||||
def test_render_with_possible_json_value_with_template_error_value(self):
|
||||
"""."""
|
||||
"""Render with possible JSON value with template error value."""
|
||||
self.assertEqual(
|
||||
'-',
|
||||
template.render_with_possible_json_value(
|
||||
self.hass, '{{ value_json', 'hello', '-'))
|
||||
|
||||
def test_raise_exception_on_error(self):
|
||||
"""."""
|
||||
"""Test raising an exception on error."""
|
||||
with self.assertRaises(TemplateError):
|
||||
template.render(self.hass, '{{ invalid_syntax')
|
||||
|
||||
def test_if_state_exists(self):
|
||||
"""."""
|
||||
"""Test if state exists works."""
|
||||
self.hass.states.set('test.object', 'available')
|
||||
self.assertEqual(
|
||||
'exists',
|
||||
@ -234,7 +234,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
"""))
|
||||
|
||||
def test_is_state(self):
|
||||
"""."""
|
||||
"""Test is_state method."""
|
||||
self.hass.states.set('test.object', 'available')
|
||||
self.assertEqual(
|
||||
'yes',
|
||||
@ -245,7 +245,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
"""))
|
||||
|
||||
def test_is_state_attr(self):
|
||||
"""."""
|
||||
"""Test is_state_attr method."""
|
||||
self.hass.states.set('test.object', 'available', {'mode': 'on'})
|
||||
self.assertEqual(
|
||||
'yes',
|
||||
@ -256,7 +256,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
"""))
|
||||
|
||||
def test_states_function(self):
|
||||
"""."""
|
||||
"""Test using states as a function."""
|
||||
self.hass.states.set('test.object', 'available')
|
||||
self.assertEqual(
|
||||
'available',
|
||||
@ -265,32 +265,26 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'unknown',
|
||||
template.render(self.hass, '{{ states("test.object2") }}'))
|
||||
|
||||
@patch('homeassistant.core.dt_util.utcnow', return_value=dt_util.utcnow())
|
||||
@patch('homeassistant.core.dt_util.now', return_value=dt_util.now())
|
||||
@patch('homeassistant.helpers.template.TemplateEnvironment.'
|
||||
'is_safe_callable', return_value=True)
|
||||
def test_now(self, mock_is_safe, mock_utcnow):
|
||||
"""."""
|
||||
"""Test now method."""
|
||||
self.assertEqual(
|
||||
dt_util.utcnow().isoformat(),
|
||||
template.render(self.hass, '{{ now.isoformat() }}'))
|
||||
dt_util.now().isoformat(),
|
||||
template.render(self.hass, '{{ now().isoformat() }}'))
|
||||
|
||||
@patch('homeassistant.core.dt_util.utcnow', return_value=dt_util.utcnow())
|
||||
@patch('homeassistant.helpers.template.TemplateEnvironment.'
|
||||
'is_safe_callable', return_value=True)
|
||||
def test_utcnow(self, mock_is_safe, mock_utcnow):
|
||||
"""."""
|
||||
"""Test utcnow method."""
|
||||
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 }}'))
|
||||
template.render(self.hass, '{{ utcnow().isoformat() }}'))
|
||||
|
||||
def test_distance_function_with_1_state(self):
|
||||
"""."""
|
||||
"""Test distance function with 1 state."""
|
||||
self.hass.states.set('test.object', 'happy', {
|
||||
'latitude': 32.87336,
|
||||
'longitude': -117.22943,
|
||||
@ -302,7 +296,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
self.hass, '{{ distance(states.test.object) | round }}'))
|
||||
|
||||
def test_distance_function_with_2_states(self):
|
||||
"""."""
|
||||
"""Test distance function with 2 states."""
|
||||
self.hass.states.set('test.object', 'happy', {
|
||||
'latitude': 32.87336,
|
||||
'longitude': -117.22943,
|
||||
@ -321,14 +315,14 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'| round }}'))
|
||||
|
||||
def test_distance_function_with_1_coord(self):
|
||||
"""."""
|
||||
"""Test distance function with 1 coord."""
|
||||
self.assertEqual(
|
||||
'187',
|
||||
template.render(
|
||||
self.hass, '{{ distance("32.87336", "-117.22943") | round }}'))
|
||||
|
||||
def test_distance_function_with_2_coords(self):
|
||||
"""."""
|
||||
"""Test distance function with 2 coords."""
|
||||
self.assertEqual(
|
||||
'187',
|
||||
template.render(
|
||||
@ -337,7 +331,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
% (self.hass.config.latitude, self.hass.config.longitude)))
|
||||
|
||||
def test_distance_function_with_1_state_1_coord(self):
|
||||
"""."""
|
||||
"""Test distance function with 1 state 1 coord."""
|
||||
self.hass.states.set('test.object_2', 'happy', {
|
||||
'latitude': self.hass.config.latitude,
|
||||
'longitude': self.hass.config.longitude,
|
||||
@ -358,7 +352,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'| round }}'))
|
||||
|
||||
def test_distance_function_return_None_if_invalid_state(self):
|
||||
"""."""
|
||||
"""Test distance function return None if invalid state."""
|
||||
self.hass.states.set('test.object_2', 'happy', {
|
||||
'latitude': 10,
|
||||
})
|
||||
@ -370,7 +364,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ distance(states.test.object_2) | round }}'))
|
||||
|
||||
def test_distance_function_return_None_if_invalid_coord(self):
|
||||
"""."""
|
||||
"""Test distance function return None if invalid coord."""
|
||||
self.assertEqual(
|
||||
'None',
|
||||
template.render(
|
||||
@ -395,7 +389,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ distance("123", states.test_object_2) }}'))
|
||||
|
||||
def test_closest_function_home_vs_domain(self):
|
||||
"""."""
|
||||
"""Test closest function home vs domain."""
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -412,7 +406,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ closest(states.test_domain).entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_all_states(self):
|
||||
"""."""
|
||||
"""Test closest function home vs all states."""
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -429,7 +423,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ closest(states).entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_group_entity_id(self):
|
||||
"""."""
|
||||
"""Test closest function home vs group entity id."""
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -448,7 +442,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ closest("group.location_group").entity_id }}'))
|
||||
|
||||
def test_closest_function_home_vs_group_state(self):
|
||||
"""."""
|
||||
"""Test closest function home vs group state."""
|
||||
self.hass.states.set('test_domain.object', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -468,7 +462,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ closest(states.group.location_group).entity_id }}'))
|
||||
|
||||
def test_closest_function_to_coord(self):
|
||||
"""."""
|
||||
"""Test closest function to coord."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -494,7 +488,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_closest_function_to_entity_id(self):
|
||||
"""."""
|
||||
"""Test closest function to entity id."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -518,7 +512,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_closest_function_to_state(self):
|
||||
"""."""
|
||||
"""Test closest function to state."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -543,7 +537,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_closest_function_invalid_state(self):
|
||||
"""."""
|
||||
"""Test closest function invalid state."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -556,7 +550,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
self.hass, '{{ closest(%s, states) }}' % state))
|
||||
|
||||
def test_closest_function_state_with_invalid_location(self):
|
||||
"""."""
|
||||
"""Test closest function state with invalid location."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': 'invalid latitude',
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -570,7 +564,7 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'states) }}'))
|
||||
|
||||
def test_closest_function_invalid_coordinates(self):
|
||||
"""."""
|
||||
"""Test closest function invalid coordinates."""
|
||||
self.hass.states.set('test_domain.closest_home', 'happy', {
|
||||
'latitude': self.hass.config.latitude + 0.1,
|
||||
'longitude': self.hass.config.longitude + 0.1,
|
||||
@ -582,6 +576,16 @@ class TestUtilTemplate(unittest.TestCase):
|
||||
'{{ closest("invalid", "coord", states) }}'))
|
||||
|
||||
def test_closest_function_no_location_states(self):
|
||||
"""."""
|
||||
"""Test closest function without location states."""
|
||||
self.assertEqual('None',
|
||||
template.render(self.hass, '{{ closest(states) }}'))
|
||||
|
||||
def test_compiling_template(self):
|
||||
"""Test compiling a template."""
|
||||
self.hass.states.set('test_domain.hello', 'world')
|
||||
compiled = template.compile_template(
|
||||
self.hass, '{{ states.test_domain.hello.state }}')
|
||||
|
||||
with patch('homeassistant.helpers.template.compile_template',
|
||||
side_effect=Exception('Should not be called')):
|
||||
assert 'world' == template.render(self.hass, compiled)
|
||||
|
Loading…
x
Reference in New Issue
Block a user