Use templates in service calls
This commit is contained in:
parent
cfd65e48cb
commit
81e5a852f0
3 changed files with 78 additions and 8 deletions
|
@ -11,13 +11,14 @@ from homeassistant.bootstrap import prepare_setup_platform
|
||||||
from homeassistant.const import CONF_PLATFORM
|
from homeassistant.const import CONF_PLATFORM
|
||||||
from homeassistant.components import logbook
|
from homeassistant.components import logbook
|
||||||
from homeassistant.helpers.service import call_from_config
|
from homeassistant.helpers.service import call_from_config
|
||||||
|
from homeassistant.helpers.service import validate_service_call
|
||||||
|
|
||||||
|
|
||||||
DOMAIN = 'automation'
|
DOMAIN = 'automation'
|
||||||
|
|
||||||
DEPENDENCIES = ['group']
|
DEPENDENCIES = ['group']
|
||||||
|
|
||||||
CONF_ALIAS = 'alias'
|
CONF_ALIAS = 'alias'
|
||||||
CONF_SERVICE = 'service'
|
|
||||||
|
|
||||||
CONF_CONDITION = 'condition'
|
CONF_CONDITION = 'condition'
|
||||||
CONF_ACTION = 'action'
|
CONF_ACTION = 'action'
|
||||||
|
@ -82,8 +83,9 @@ def _setup_automation(hass, config_block, name, config):
|
||||||
|
|
||||||
def _get_action(hass, config, name):
|
def _get_action(hass, config, name):
|
||||||
"""Return an action based on a configuration."""
|
"""Return an action based on a configuration."""
|
||||||
if CONF_SERVICE not in config:
|
validation_error = validate_service_call(config)
|
||||||
_LOGGER.error('Error setting up %s, no action specified.', name)
|
if validation_error:
|
||||||
|
_LOGGER.error(validation_error)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def action():
|
def action():
|
||||||
|
|
|
@ -3,14 +3,16 @@ import functools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.helpers.entity import split_entity_id
|
from homeassistant.helpers import template
|
||||||
from homeassistant.loader import get_component
|
from homeassistant.loader import get_component
|
||||||
|
|
||||||
HASS = None
|
HASS = None
|
||||||
|
|
||||||
CONF_SERVICE = 'service'
|
CONF_SERVICE = 'service'
|
||||||
|
CONF_SERVICE_TEMPLATE = 'service_template'
|
||||||
CONF_SERVICE_ENTITY_ID = 'entity_id'
|
CONF_SERVICE_ENTITY_ID = 'entity_id'
|
||||||
CONF_SERVICE_DATA = 'data'
|
CONF_SERVICE_DATA = 'data'
|
||||||
|
CONF_SERVICE_DATA_TEMPLATE = 'data_template'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -29,14 +31,20 @@ def service(domain, service_name):
|
||||||
|
|
||||||
def call_from_config(hass, config, blocking=False):
|
def call_from_config(hass, config, blocking=False):
|
||||||
"""Call a service based on a config hash."""
|
"""Call a service based on a config hash."""
|
||||||
if not isinstance(config, dict) or CONF_SERVICE not in config:
|
validation_error = validate_service_call(config)
|
||||||
_LOGGER.error('Missing key %s: %s', CONF_SERVICE, config)
|
if validation_error:
|
||||||
|
_LOGGER.error(validation_error)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
domain_service = (
|
||||||
|
config[CONF_SERVICE]
|
||||||
|
if CONF_SERVICE in config
|
||||||
|
else template.render(hass, config[CONF_SERVICE_TEMPLATE]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
domain, service_name = split_entity_id(config[CONF_SERVICE])
|
domain, service_name = domain_service.split('.', 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error('Invalid service specified: %s', config[CONF_SERVICE])
|
_LOGGER.error('Invalid service specified: %s', domain_service)
|
||||||
return
|
return
|
||||||
|
|
||||||
service_data = config.get(CONF_SERVICE_DATA)
|
service_data = config.get(CONF_SERVICE_DATA)
|
||||||
|
@ -49,6 +57,13 @@ def call_from_config(hass, config, blocking=False):
|
||||||
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
|
_LOGGER.error("%s should be a dictionary", CONF_SERVICE_DATA)
|
||||||
service_data = {}
|
service_data = {}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
entity_id = config.get(CONF_SERVICE_ENTITY_ID)
|
entity_id = config.get(CONF_SERVICE_ENTITY_ID)
|
||||||
if isinstance(entity_id, str):
|
if isinstance(entity_id, str):
|
||||||
service_data[ATTR_ENTITY_ID] = [ent.strip() for ent in
|
service_data[ATTR_ENTITY_ID] = [ent.strip() for ent in
|
||||||
|
@ -76,3 +91,19 @@ def extract_entity_ids(hass, service_call):
|
||||||
return group.expand_entity_ids(hass, [service_ent_id])
|
return group.expand_entity_ids(hass, [service_ent_id])
|
||||||
|
|
||||||
return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)]
|
return [ent_id for ent_id in group.expand_entity_ids(hass, service_ent_id)]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_service_call(config):
|
||||||
|
"""Validate service call configuration.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -39,6 +39,25 @@ class TestServiceHelpers(unittest.TestCase):
|
||||||
self.hass.pool.block_till_done()
|
self.hass.pool.block_till_done()
|
||||||
self.assertEqual(1, len(runs))
|
self.assertEqual(1, len(runs))
|
||||||
|
|
||||||
|
def test_template_service_call(self):
|
||||||
|
""" Test service call with tempating. """
|
||||||
|
config = {
|
||||||
|
'service_template': '{{ \'test_domain.test_service\' }}',
|
||||||
|
'entity_id': 'hello.world',
|
||||||
|
'data_template': {
|
||||||
|
'hello': '{{ \'goodbye\' }}',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runs = []
|
||||||
|
|
||||||
|
decor = service.service('test_domain', 'test_service')
|
||||||
|
decor(lambda x, y: runs.append(y))
|
||||||
|
|
||||||
|
service.call_from_config(self.hass, config)
|
||||||
|
self.hass.pool.block_till_done()
|
||||||
|
|
||||||
|
self.assertEqual('goodbye', runs[0].data['hello'])
|
||||||
|
|
||||||
def test_split_entity_string(self):
|
def test_split_entity_string(self):
|
||||||
service.call_from_config(self.hass, {
|
service.call_from_config(self.hass, {
|
||||||
'service': 'test_domain.test_service',
|
'service': 'test_domain.test_service',
|
||||||
|
@ -99,3 +118,21 @@ class TestServiceHelpers(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(['light.ceiling', 'light.kitchen'],
|
self.assertEqual(['light.ceiling', 'light.kitchen'],
|
||||||
service.extract_entity_ids(self.hass, call))
|
service.extract_entity_ids(self.hass, call))
|
||||||
|
|
||||||
|
def test_validate_service_call(self):
|
||||||
|
"""Test is_valid_service_call method"""
|
||||||
|
self.assertNotEqual(
|
||||||
|
service.validate_service_call(
|
||||||
|
{}),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
service.validate_service_call(
|
||||||
|
{'service': 'test_domain.test_service'}),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
service.validate_service_call(
|
||||||
|
{'service_template': 'test_domain.{{ \'test_service\' }}'}),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue