mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add a service target (#43725)
This commit is contained in:
parent
a2e1efca33
commit
dd513147a5
@ -9,9 +9,10 @@ blueprint:
|
||||
entity:
|
||||
domain: binary_sensor
|
||||
device_class: motion
|
||||
light_entity:
|
||||
light_target:
|
||||
name: Light
|
||||
selector:
|
||||
target:
|
||||
entity:
|
||||
domain: light
|
||||
|
||||
@ -28,7 +29,7 @@ trigger:
|
||||
|
||||
action:
|
||||
- service: homeassistant.turn_on
|
||||
entity_id: !placeholder light_entity
|
||||
target: !placeholder light_target
|
||||
- wait_for_trigger:
|
||||
platform: state
|
||||
entity_id: !placeholder motion_entity
|
||||
@ -36,4 +37,4 @@ action:
|
||||
to: "off"
|
||||
- delay: 120
|
||||
- service: homeassistant.turn_off
|
||||
entity_id: !placeholder light_entity
|
||||
target: !placeholder light_target
|
||||
|
@ -170,6 +170,7 @@ CONF_STATE = "state"
|
||||
CONF_STATE_TEMPLATE = "state_template"
|
||||
CONF_STRUCTURE = "structure"
|
||||
CONF_SWITCHES = "switches"
|
||||
CONF_TARGET = "target"
|
||||
CONF_TEMPERATURE_UNIT = "temperature_unit"
|
||||
CONF_TIMEOUT = "timeout"
|
||||
CONF_TIME_ZONE = "time_zone"
|
||||
|
@ -62,6 +62,7 @@ from homeassistant.const import (
|
||||
CONF_SERVICE,
|
||||
CONF_SERVICE_TEMPLATE,
|
||||
CONF_STATE,
|
||||
CONF_TARGET,
|
||||
CONF_TIMEOUT,
|
||||
CONF_UNIT_SYSTEM_IMPERIAL,
|
||||
CONF_UNIT_SYSTEM_METRIC,
|
||||
@ -881,7 +882,10 @@ PLATFORM_SCHEMA = vol.Schema(
|
||||
|
||||
PLATFORM_SCHEMA_BASE = PLATFORM_SCHEMA.extend({}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
ENTITY_SERVICE_FIELDS = (ATTR_ENTITY_ID, ATTR_AREA_ID)
|
||||
ENTITY_SERVICE_FIELDS = {
|
||||
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids,
|
||||
vol.Optional(ATTR_AREA_ID): vol.Any(ENTITY_MATCH_NONE, vol.All(ensure_list, [str])),
|
||||
}
|
||||
|
||||
|
||||
def make_entity_service_schema(
|
||||
@ -892,10 +896,7 @@ def make_entity_service_schema(
|
||||
vol.Schema(
|
||||
{
|
||||
**schema,
|
||||
vol.Optional(ATTR_ENTITY_ID): comp_entity_ids,
|
||||
vol.Optional(ATTR_AREA_ID): vol.Any(
|
||||
ENTITY_MATCH_NONE, vol.All(ensure_list, [str])
|
||||
),
|
||||
**ENTITY_SERVICE_FIELDS,
|
||||
},
|
||||
extra=extra,
|
||||
),
|
||||
@ -942,6 +943,7 @@ SERVICE_SCHEMA = vol.All(
|
||||
vol.Optional("data"): vol.All(dict, template_complex),
|
||||
vol.Optional("data_template"): vol.All(dict, template_complex),
|
||||
vol.Optional(CONF_ENTITY_ID): comp_entity_ids,
|
||||
vol.Optional(CONF_TARGET): ENTITY_SERVICE_FIELDS,
|
||||
}
|
||||
),
|
||||
has_at_least_one_key(CONF_SERVICE, CONF_SERVICE_TEMPLATE),
|
||||
|
@ -111,3 +111,13 @@ class TimeSelector(Selector):
|
||||
"""Selector of a time value."""
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({})
|
||||
|
||||
|
||||
@SELECTORS.register("target")
|
||||
class TargetSelector(Selector):
|
||||
"""Selector of a target value (area ID, device ID, entity ID etc).
|
||||
|
||||
Value should follow cv.ENTITY_SERVICE_FIELDS format.
|
||||
"""
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({"entity": {"domain": str}})
|
||||
|
@ -24,6 +24,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_SERVICE,
|
||||
CONF_SERVICE_TEMPLATE,
|
||||
CONF_TARGET,
|
||||
ENTITY_MATCH_ALL,
|
||||
ENTITY_MATCH_NONE,
|
||||
)
|
||||
@ -136,6 +137,10 @@ def async_prepare_call_from_config(
|
||||
domain, service = domain_service.split(".", 1)
|
||||
|
||||
service_data = {}
|
||||
|
||||
if CONF_TARGET in config:
|
||||
service_data.update(config[CONF_TARGET])
|
||||
|
||||
for conf in [CONF_SERVICE_DATA, CONF_SERVICE_DATA_TEMPLATE]:
|
||||
if conf not in config:
|
||||
continue
|
||||
|
@ -123,7 +123,7 @@ async def test_motion_light(hass):
|
||||
"use_blueprint": {
|
||||
"path": "motion_light.yaml",
|
||||
"input": {
|
||||
"light_entity": "light.kitchen",
|
||||
"light_target": {"entity_id": "light.kitchen"},
|
||||
"motion_entity": "binary_sensor.kitchen",
|
||||
},
|
||||
}
|
||||
|
@ -117,3 +117,16 @@ def test_boolean_selector_schema(schema):
|
||||
def test_time_selector_schema(schema):
|
||||
"""Test time selector."""
|
||||
selector.validate_selector({"time": schema})
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"schema",
|
||||
(
|
||||
{},
|
||||
{"entity": {}},
|
||||
{"entity": {"domain": "light"}},
|
||||
),
|
||||
)
|
||||
def test_target_selector_schema(schema):
|
||||
"""Test entity selector."""
|
||||
selector.validate_selector({"target": schema})
|
||||
|
@ -175,18 +175,25 @@ class TestServiceHelpers(unittest.TestCase):
|
||||
"entity_id": "hello.world",
|
||||
"data": {
|
||||
"hello": "{{ 'goodbye' }}",
|
||||
"data": {"value": "{{ 'complex' }}", "simple": "simple"},
|
||||
"effect": {"value": "{{ 'complex' }}", "simple": "simple"},
|
||||
},
|
||||
"data_template": {"list": ["{{ 'list' }}", "2"]},
|
||||
"target": {"area_id": "test-area-id", "entity_id": "will.be_overridden"},
|
||||
}
|
||||
|
||||
service.call_from_config(self.hass, config)
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert self.calls[0].data["hello"] == "goodbye"
|
||||
assert self.calls[0].data["data"]["value"] == "complex"
|
||||
assert self.calls[0].data["data"]["simple"] == "simple"
|
||||
assert self.calls[0].data["list"][0] == "list"
|
||||
assert dict(self.calls[0].data) == {
|
||||
"hello": "goodbye",
|
||||
"effect": {
|
||||
"value": "complex",
|
||||
"simple": "simple",
|
||||
},
|
||||
"list": ["list", "2"],
|
||||
"entity_id": ["hello.world"],
|
||||
"area_id": ["test-area-id"],
|
||||
}
|
||||
|
||||
def test_service_template_service_call(self):
|
||||
"""Test legacy service_template call with templating."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user