Allow variables in service.call_from_config
parent
3318c55c65
commit
4acb121689
homeassistant/helpers
tests/helpers
|
@ -231,8 +231,8 @@ EVENT_SCHEMA = vol.Schema({
|
|||
|
||||
SERVICE_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Exclusive('service', 'service name'): service,
|
||||
vol.Exclusive('service_template', 'service name'): string,
|
||||
vol.Exclusive('data', 'service data'): dict,
|
||||
vol.Exclusive('data_template', 'service data'): {match_all: template},
|
||||
'entity_id': entity_ids,
|
||||
vol.Exclusive('service_template', 'service name'): template,
|
||||
vol.Optional('data'): dict,
|
||||
vol.Optional('data_template'): {match_all: template},
|
||||
vol.Optional('entity_id'): entity_ids,
|
||||
}), has_at_least_one_key('service', 'service_template'))
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
import functools
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import template
|
||||
from homeassistant.loader import get_component
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
HASS = None
|
||||
|
||||
|
@ -28,47 +32,38 @@ def service(domain, service_name):
|
|||
return register_service_decorator
|
||||
|
||||
|
||||
def call_from_config(hass, config, blocking=False):
|
||||
def call_from_config(hass, config, blocking=False, variables=None):
|
||||
"""Call a service based on a config hash."""
|
||||
validation_error = validate_service_call(config)
|
||||
if validation_error:
|
||||
_LOGGER.error(validation_error)
|
||||
return
|
||||
|
||||
domain_service = (
|
||||
config[CONF_SERVICE]
|
||||
if CONF_SERVICE in config
|
||||
else template.render(hass, config[CONF_SERVICE_TEMPLATE]))
|
||||
|
||||
try:
|
||||
domain, service_name = domain_service.split('.', 1)
|
||||
except ValueError:
|
||||
_LOGGER.error('Invalid service specified: %s', domain_service)
|
||||
config = cv.SERVICE_SCHEMA(config)
|
||||
except vol.Invalid as ex:
|
||||
_LOGGER.error("Invalid config for calling service: %s", ex)
|
||||
return
|
||||
|
||||
service_data = config.get(CONF_SERVICE_DATA)
|
||||
|
||||
if service_data is None:
|
||||
service_data = {}
|
||||
elif isinstance(service_data, dict):
|
||||
service_data = dict(service_data)
|
||||
if CONF_SERVICE in config:
|
||||
domain_service = config[CONF_SERVICE]
|
||||
else:
|
||||
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
|
||||
service_data = {}
|
||||
try:
|
||||
domain_service = template.render(
|
||||
hass, config[CONF_SERVICE_TEMPLATE], variables)
|
||||
domain_service = cv.service(domain_service)
|
||||
except TemplateError as ex:
|
||||
_LOGGER.error('Error rendering service name template: %s', ex)
|
||||
return
|
||||
except vol.Invalid as ex:
|
||||
_LOGGER.error('Template rendered invalid service: %s',
|
||||
domain_service)
|
||||
return
|
||||
|
||||
service_data_template = config.get(CONF_SERVICE_DATA_TEMPLATE)
|
||||
if service_data_template and isinstance(service_data_template, dict):
|
||||
for key, value in service_data_template.items():
|
||||
service_data[key] = template.render(hass, value)
|
||||
elif service_data_template:
|
||||
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
|
||||
domain, service_name = domain_service.split('.', 1)
|
||||
service_data = dict(config.get(CONF_SERVICE_DATA, {}))
|
||||
|
||||
entity_id = config.get(CONF_SERVICE_ENTITY_ID)
|
||||
if isinstance(entity_id, str):
|
||||
service_data[ATTR_ENTITY_ID] = [ent.strip() for ent in
|
||||
entity_id.split(",")]
|
||||
elif entity_id is not None:
|
||||
service_data[ATTR_ENTITY_ID] = entity_id
|
||||
if CONF_SERVICE_DATA_TEMPLATE in config:
|
||||
for key, value in config[CONF_SERVICE_DATA_TEMPLATE].items():
|
||||
service_data[key] = template.render(hass, value, variables)
|
||||
|
||||
if CONF_SERVICE_ENTITY_ID in config:
|
||||
service_data[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
|
||||
|
||||
hass.services.call(domain, service_name, service_data, blocking)
|
||||
|
||||
|
@ -98,11 +93,8 @@ def validate_service_call(config):
|
|||
Helper method to validate that a configuration is a valid service call.
|
||||
Returns None if validation succeeds, else an error description
|
||||
"""
|
||||
if not isinstance(config, dict):
|
||||
return 'Invalid configuration {}'.format(config)
|
||||
if CONF_SERVICE not in config and CONF_SERVICE_TEMPLATE not in config:
|
||||
return 'Missing key {} or {}: {}'.format(
|
||||
CONF_SERVICE,
|
||||
CONF_SERVICE_TEMPLATE,
|
||||
config)
|
||||
return None
|
||||
try:
|
||||
cv.SERVICE_SCHEMA(config)
|
||||
return None
|
||||
except vol.Invalid as ex:
|
||||
return str(ex)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import homeassistant.components # noqa - to prevent circular import
|
||||
from homeassistant import core as ha, loader
|
||||
from homeassistant.const import STATE_ON, STATE_OFF, ATTR_ENTITY_ID
|
||||
from homeassistant.helpers import service
|
||||
|
@ -53,6 +54,27 @@ class TestServiceHelpers(unittest.TestCase):
|
|||
|
||||
self.assertEqual('goodbye', runs[0].data['hello'])
|
||||
|
||||
def test_passing_variables_to_templates(self):
|
||||
config = {
|
||||
'service_template': '{{ var_service }}',
|
||||
'entity_id': 'hello.world',
|
||||
'data_template': {
|
||||
'hello': '{{ var_data }}',
|
||||
},
|
||||
}
|
||||
runs = []
|
||||
|
||||
decor = service.service('test_domain', 'test_service')
|
||||
decor(lambda x, y: runs.append(y))
|
||||
|
||||
service.call_from_config(self.hass, config, variables={
|
||||
'var_service': 'test_domain.test_service',
|
||||
'var_data': 'goodbye',
|
||||
})
|
||||
self.hass.pool.block_till_done()
|
||||
|
||||
self.assertEqual('goodbye', runs[0].data['hello'])
|
||||
|
||||
def test_split_entity_string(self):
|
||||
"""Test splitting of entity string."""
|
||||
service.call_from_config(self.hass, {
|
||||
|
|
Loading…
Reference in New Issue