mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 10:47:10 +00:00
Add support for multiple entity_ids in conditions (#36817)
This commit is contained in:
parent
0a219081ea
commit
ba73ac12ba
@ -238,7 +238,7 @@ def async_numeric_state_from_config(
|
|||||||
"""Wrap action method with state based condition."""
|
"""Wrap action method with state based condition."""
|
||||||
if config_validation:
|
if config_validation:
|
||||||
config = cv.NUMERIC_STATE_CONDITION_SCHEMA(config)
|
config = cv.NUMERIC_STATE_CONDITION_SCHEMA(config)
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_ids = config.get(CONF_ENTITY_ID, [])
|
||||||
below = config.get(CONF_BELOW)
|
below = config.get(CONF_BELOW)
|
||||||
above = config.get(CONF_ABOVE)
|
above = config.get(CONF_ABOVE)
|
||||||
value_template = config.get(CONF_VALUE_TEMPLATE)
|
value_template = config.get(CONF_VALUE_TEMPLATE)
|
||||||
@ -250,8 +250,11 @@ def async_numeric_state_from_config(
|
|||||||
if value_template is not None:
|
if value_template is not None:
|
||||||
value_template.hass = hass
|
value_template.hass = hass
|
||||||
|
|
||||||
return async_numeric_state(
|
return all(
|
||||||
hass, entity_id, below, above, value_template, variables
|
async_numeric_state(
|
||||||
|
hass, entity_id, below, above, value_template, variables
|
||||||
|
)
|
||||||
|
for entity_id in entity_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
return if_numeric_state
|
return if_numeric_state
|
||||||
@ -288,13 +291,15 @@ def state_from_config(
|
|||||||
"""Wrap action method with state based condition."""
|
"""Wrap action method with state based condition."""
|
||||||
if config_validation:
|
if config_validation:
|
||||||
config = cv.STATE_CONDITION_SCHEMA(config)
|
config = cv.STATE_CONDITION_SCHEMA(config)
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_ids = config.get(CONF_ENTITY_ID, [])
|
||||||
req_state = cast(str, config.get(CONF_STATE))
|
req_state = cast(str, config.get(CONF_STATE))
|
||||||
for_period = config.get("for")
|
for_period = config.get("for")
|
||||||
|
|
||||||
def if_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
def if_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
||||||
"""Test if condition."""
|
"""Test if condition."""
|
||||||
return state(hass, entity_id, req_state, for_period)
|
return all(
|
||||||
|
state(hass, entity_id, req_state, for_period) for entity_id in entity_ids
|
||||||
|
)
|
||||||
|
|
||||||
return if_state
|
return if_state
|
||||||
|
|
||||||
@ -506,12 +511,12 @@ def zone_from_config(
|
|||||||
"""Wrap action method with zone based condition."""
|
"""Wrap action method with zone based condition."""
|
||||||
if config_validation:
|
if config_validation:
|
||||||
config = cv.ZONE_CONDITION_SCHEMA(config)
|
config = cv.ZONE_CONDITION_SCHEMA(config)
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_ids = config.get(CONF_ENTITY_ID, [])
|
||||||
zone_entity_id = config.get(CONF_ZONE)
|
zone_entity_id = config.get(CONF_ZONE)
|
||||||
|
|
||||||
def if_in_zone(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
def if_in_zone(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
||||||
"""Test if condition."""
|
"""Test if condition."""
|
||||||
return zone(hass, zone_entity_id, entity_id)
|
return all(zone(hass, zone_entity_id, entity_id) for entity_id in entity_ids)
|
||||||
|
|
||||||
return if_in_zone
|
return if_in_zone
|
||||||
|
|
||||||
@ -556,7 +561,7 @@ async def async_validate_condition_config(
|
|||||||
@callback
|
@callback
|
||||||
def async_extract_entities(config: ConfigType) -> Set[str]:
|
def async_extract_entities(config: ConfigType) -> Set[str]:
|
||||||
"""Extract entities from a condition."""
|
"""Extract entities from a condition."""
|
||||||
referenced = set()
|
referenced: Set[str] = set()
|
||||||
to_process = deque([config])
|
to_process = deque([config])
|
||||||
|
|
||||||
while to_process:
|
while to_process:
|
||||||
@ -567,10 +572,13 @@ def async_extract_entities(config: ConfigType) -> Set[str]:
|
|||||||
to_process.extend(config["conditions"])
|
to_process.extend(config["conditions"])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entity_id = config.get(CONF_ENTITY_ID)
|
entity_ids = config.get(CONF_ENTITY_ID)
|
||||||
|
|
||||||
if entity_id is not None:
|
if isinstance(entity_ids, str):
|
||||||
referenced.add(entity_id)
|
entity_ids = [entity_ids]
|
||||||
|
|
||||||
|
if entity_ids is not None:
|
||||||
|
referenced.update(entity_ids)
|
||||||
|
|
||||||
return referenced
|
return referenced
|
||||||
|
|
||||||
|
@ -844,7 +844,7 @@ NUMERIC_STATE_CONDITION_SCHEMA = vol.All(
|
|||||||
vol.Schema(
|
vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_CONDITION): "numeric_state",
|
vol.Required(CONF_CONDITION): "numeric_state",
|
||||||
vol.Required(CONF_ENTITY_ID): entity_id,
|
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||||
CONF_BELOW: vol.Coerce(float),
|
CONF_BELOW: vol.Coerce(float),
|
||||||
CONF_ABOVE: vol.Coerce(float),
|
CONF_ABOVE: vol.Coerce(float),
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): template,
|
vol.Optional(CONF_VALUE_TEMPLATE): template,
|
||||||
@ -857,7 +857,7 @@ STATE_CONDITION_SCHEMA = vol.All(
|
|||||||
vol.Schema(
|
vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_CONDITION): "state",
|
vol.Required(CONF_CONDITION): "state",
|
||||||
vol.Required(CONF_ENTITY_ID): entity_id,
|
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||||
vol.Required(CONF_STATE): str,
|
vol.Required(CONF_STATE): str,
|
||||||
vol.Optional(CONF_FOR): vol.All(time_period, positive_timedelta),
|
vol.Optional(CONF_FOR): vol.All(time_period, positive_timedelta),
|
||||||
# To support use_trigger_value in automation
|
# To support use_trigger_value in automation
|
||||||
@ -905,7 +905,7 @@ TIME_CONDITION_SCHEMA = vol.All(
|
|||||||
ZONE_CONDITION_SCHEMA = vol.Schema(
|
ZONE_CONDITION_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_CONDITION): "zone",
|
vol.Required(CONF_CONDITION): "zone",
|
||||||
vol.Required(CONF_ENTITY_ID): entity_id,
|
vol.Required(CONF_ENTITY_ID): entity_ids,
|
||||||
"zone": entity_id,
|
"zone": entity_id,
|
||||||
# To support use_trigger_value in automation
|
# To support use_trigger_value in automation
|
||||||
# Deprecated 2016/04/25
|
# Deprecated 2016/04/25
|
||||||
|
@ -266,6 +266,123 @@ async def test_if_numeric_state_not_raise_on_unavailable(hass):
|
|||||||
assert len(logwarn.mock_calls) == 0
|
assert len(logwarn.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_state_multiple_entities(hass):
|
||||||
|
"""Test with multiple entities in condition."""
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "and",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "state",
|
||||||
|
"entity_id": ["sensor.temperature_1", "sensor.temperature_2"],
|
||||||
|
"state": "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 100)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 100)
|
||||||
|
assert test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 101)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 100)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 100)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 101)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_numeric_state_multiple_entities(hass):
|
||||||
|
"""Test with multiple entities in condition."""
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "and",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": ["sensor.temperature_1", "sensor.temperature_2"],
|
||||||
|
"below": 50,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 49)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 49)
|
||||||
|
assert test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 50)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 49)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set("sensor.temperature_1", 49)
|
||||||
|
hass.states.async_set("sensor.temperature_2", 50)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zone_multiple_entities(hass):
|
||||||
|
"""Test with multiple entities in condition."""
|
||||||
|
test = await condition.async_from_config(
|
||||||
|
hass,
|
||||||
|
{
|
||||||
|
"condition": "and",
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "zone",
|
||||||
|
"entity_id": ["device_tracker.person_1", "device_tracker.person_2"],
|
||||||
|
"zone": "zone.home",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"zone.home",
|
||||||
|
"zoning",
|
||||||
|
{"name": "home", "latitude": 2.1, "longitude": 1.1, "radius": 10},
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_1",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_1", "latitude": 2.1, "longitude": 1.1},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_2",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_2", "latitude": 2.1, "longitude": 1.1},
|
||||||
|
)
|
||||||
|
assert test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_1",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_1", "latitude": 20.1, "longitude": 10.1},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_2",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_2", "latitude": 2.1, "longitude": 1.1},
|
||||||
|
)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_1",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_1", "latitude": 2.1, "longitude": 1.1},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"device_tracker.person_2",
|
||||||
|
"home",
|
||||||
|
{"friendly_name": "person_2", "latitude": 20.1, "longitude": 10.1},
|
||||||
|
)
|
||||||
|
assert not test(hass)
|
||||||
|
|
||||||
|
|
||||||
async def test_extract_entities():
|
async def test_extract_entities():
|
||||||
"""Test extracting entities."""
|
"""Test extracting entities."""
|
||||||
assert condition.async_extract_entities(
|
assert condition.async_extract_entities(
|
||||||
@ -312,6 +429,16 @@ async def test_extract_entities():
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"condition": "state",
|
||||||
|
"entity_id": ["sensor.temperature_7", "sensor.temperature_8"],
|
||||||
|
"state": "100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"condition": "numeric_state",
|
||||||
|
"entity_id": ["sensor.temperature_9", "sensor.temperature_10"],
|
||||||
|
"below": 110,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
) == {
|
) == {
|
||||||
@ -321,6 +448,10 @@ async def test_extract_entities():
|
|||||||
"sensor.temperature_4",
|
"sensor.temperature_4",
|
||||||
"sensor.temperature_5",
|
"sensor.temperature_5",
|
||||||
"sensor.temperature_6",
|
"sensor.temperature_6",
|
||||||
|
"sensor.temperature_7",
|
||||||
|
"sensor.temperature_8",
|
||||||
|
"sensor.temperature_9",
|
||||||
|
"sensor.temperature_10",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user