Service config calls will no longer mutate original config (#3628)
parent
9cf2ad0b55
commit
ef0e018cbb
|
@ -62,22 +62,18 @@ def call_from_config(hass, config, blocking=False, variables=None,
|
||||||
domain, service_name = domain_service.split('.', 1)
|
domain, service_name = domain_service.split('.', 1)
|
||||||
service_data = dict(config.get(CONF_SERVICE_DATA, {}))
|
service_data = dict(config.get(CONF_SERVICE_DATA, {}))
|
||||||
|
|
||||||
def _data_template_creator(value):
|
|
||||||
"""Recursive template creator helper function."""
|
|
||||||
if isinstance(value, list):
|
|
||||||
for idx, element in enumerate(value):
|
|
||||||
value[idx] = _data_template_creator(element)
|
|
||||||
return value
|
|
||||||
if isinstance(value, dict):
|
|
||||||
for key, element in value.items():
|
|
||||||
value[key] = _data_template_creator(element)
|
|
||||||
return value
|
|
||||||
value.hass = hass
|
|
||||||
return value.render(variables)
|
|
||||||
|
|
||||||
if CONF_SERVICE_DATA_TEMPLATE in config:
|
if CONF_SERVICE_DATA_TEMPLATE in config:
|
||||||
for key, value in config[CONF_SERVICE_DATA_TEMPLATE].items():
|
def _data_template_creator(value):
|
||||||
service_data[key] = _data_template_creator(value)
|
"""Recursive template creator helper function."""
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [_data_template_creator(item) for item in value]
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
return {key: _data_template_creator(item)
|
||||||
|
for key, item in value.items()}
|
||||||
|
value.hass = hass
|
||||||
|
return value.render(variables)
|
||||||
|
service_data.update(_data_template_creator(
|
||||||
|
config[CONF_SERVICE_DATA_TEMPLATE]))
|
||||||
|
|
||||||
if CONF_SERVICE_ENTITY_ID in config:
|
if CONF_SERVICE_ENTITY_ID in config:
|
||||||
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
|
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
|
||||||
|
|
|
@ -159,6 +159,12 @@ class Template(object):
|
||||||
|
|
||||||
return self._compiled
|
return self._compiled
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Compare template with another."""
|
||||||
|
return (self.__class__ == other.__class__ and
|
||||||
|
self.template == other.template and
|
||||||
|
self.hass == other.hass)
|
||||||
|
|
||||||
|
|
||||||
class AllStates(object):
|
class AllStates(object):
|
||||||
"""Class to expose all HA states as attributes."""
|
"""Class to expose all HA states as attributes."""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Test service helpers."""
|
"""Test service helpers."""
|
||||||
|
from copy import deepcopy
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
@ -6,7 +7,8 @@ from unittest.mock import patch
|
||||||
import homeassistant.components # noqa
|
import homeassistant.components # noqa
|
||||||
from homeassistant import core as ha, loader
|
from homeassistant import core as ha, loader
|
||||||
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
|
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
|
||||||
from homeassistant.helpers import service
|
from homeassistant.helpers import service, template
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from tests.common import get_test_home_assistant, mock_service
|
from tests.common import get_test_home_assistant, mock_service
|
||||||
|
|
||||||
|
@ -97,22 +99,25 @@ class TestServiceHelpers(unittest.TestCase):
|
||||||
|
|
||||||
def test_not_mutate_input(self):
|
def test_not_mutate_input(self):
|
||||||
"""Test for immutable input."""
|
"""Test for immutable input."""
|
||||||
orig = {
|
config = cv.SERVICE_SCHEMA({
|
||||||
'service': 'test_domain.test_service',
|
'service': 'test_domain.test_service',
|
||||||
'entity_id': 'hello.world, sensor.beer',
|
'entity_id': 'hello.world, sensor.beer',
|
||||||
'data': {
|
'data': {
|
||||||
'hello': 1,
|
'hello': 1,
|
||||||
},
|
},
|
||||||
}
|
'data_template': {
|
||||||
service.call_from_config(self.hass, orig)
|
'nested': {
|
||||||
self.hass.block_till_done()
|
'value': '{{ 1 + 1 }}'
|
||||||
self.assertEqual({
|
}
|
||||||
'service': 'test_domain.test_service',
|
}
|
||||||
'entity_id': 'hello.world, sensor.beer',
|
})
|
||||||
'data': {
|
orig = deepcopy(config)
|
||||||
'hello': 1,
|
|
||||||
},
|
# Only change after call is each template getting hass attached
|
||||||
}, orig)
|
template.attach(self.hass, orig)
|
||||||
|
|
||||||
|
service.call_from_config(self.hass, config, validate_config=False)
|
||||||
|
assert orig == config
|
||||||
|
|
||||||
@patch('homeassistant.helpers.service._LOGGER.error')
|
@patch('homeassistant.helpers.service._LOGGER.error')
|
||||||
def test_fail_silently_if_no_service(self, mock_log):
|
def test_fail_silently_if_no_service(self, mock_log):
|
||||||
|
|
Loading…
Reference in New Issue