Allow any entity to match state condition (#69763)

This commit is contained in:
Franck Nijhof 2022-04-11 19:53:42 +02:00 committed by GitHub
parent 85f698f873
commit 7087020283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 2 deletions

View File

@ -61,6 +61,7 @@ MATCH_ALL: Final = "*"
# Entity target all constant
ENTITY_MATCH_NONE: Final = "none"
ENTITY_MATCH_ALL: Final = "all"
ENTITY_MATCH_ANY: Final = "any"
# If no name is specified
DEVICE_DEFAULT_NAME: Final = "Unnamed Device"
@ -172,6 +173,7 @@ CONF_LIGHTS: Final = "lights"
CONF_LOCATION: Final = "location"
CONF_LONGITUDE: Final = "longitude"
CONF_MAC: Final = "mac"
CONF_MATCH: Final = "match"
CONF_MAXIMUM: Final = "maximum"
CONF_MEDIA_DIRS: Final = "media_dirs"
CONF_METHOD: Final = "method"

View File

@ -29,10 +29,13 @@ from homeassistant.const import (
CONF_DEVICE_ID,
CONF_ENTITY_ID,
CONF_ID,
CONF_MATCH,
CONF_STATE,
CONF_VALUE_TEMPLATE,
CONF_WEEKDAY,
CONF_ZONE,
ENTITY_MATCH_ALL,
ENTITY_MATCH_ANY,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
SUN_EVENT_SUNRISE,
@ -524,6 +527,7 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
req_states: str | list[str] = config.get(CONF_STATE, [])
for_period = config.get("for")
attribute = config.get(CONF_ATTRIBUTE)
match = config.get(CONF_MATCH, ENTITY_MATCH_ALL)
if not isinstance(req_states, list):
req_states = [req_states]
@ -532,10 +536,13 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
def if_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
"""Test if condition."""
errors = []
result: bool = match != ENTITY_MATCH_ANY
for index, entity_id in enumerate(entity_ids):
try:
with trace_path(["entity_id", str(index)]), trace_condition(variables):
if not state(hass, entity_id, req_states, for_period, attribute):
if state(hass, entity_id, req_states, for_period, attribute):
result = True
elif match == ENTITY_MATCH_ALL:
return False
except ConditionError as ex:
errors.append(
@ -548,7 +555,7 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
if errors:
raise ConditionErrorContainer("state", errors=errors)
return True
return result
return if_state

View File

@ -49,6 +49,7 @@ from homeassistant.const import (
CONF_EVENT_DATA_TEMPLATE,
CONF_FOR,
CONF_ID,
CONF_MATCH,
CONF_PLATFORM,
CONF_REPEAT,
CONF_SCAN_INTERVAL,
@ -68,6 +69,7 @@ from homeassistant.const import (
CONF_WAIT_TEMPLATE,
CONF_WHILE,
ENTITY_MATCH_ALL,
ENTITY_MATCH_ANY,
ENTITY_MATCH_NONE,
SUN_EVENT_SUNRISE,
SUN_EVENT_SUNSET,
@ -1105,6 +1107,9 @@ STATE_CONDITION_BASE_SCHEMA = {
**CONDITION_BASE_SCHEMA,
vol.Required(CONF_CONDITION): "state",
vol.Required(CONF_ENTITY_ID): entity_ids_or_uuids,
vol.Optional(CONF_MATCH, default=ENTITY_MATCH_ALL): vol.All(
vol.Lower, vol.Any(ENTITY_MATCH_ALL, ENTITY_MATCH_ANY)
),
vol.Optional(CONF_ATTRIBUTE): str,
vol.Optional(CONF_FOR): positive_time_period,
# To support use_trigger_value in automation

View File

@ -15,6 +15,7 @@ from homeassistant.const import (
SUN_EVENT_SUNRISE,
SUN_EVENT_SUNSET,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConditionError, HomeAssistantError
from homeassistant.helpers import (
condition,
@ -1020,6 +1021,40 @@ async def test_state_multiple_entities(hass):
assert not test(hass)
async def test_state_multiple_entities_match_any(hass: HomeAssistant) -> None:
"""Test with multiple entities in condition with match any."""
config = {
"condition": "and",
"conditions": [
{
"condition": "state",
"entity_id": ["sensor.temperature_1", "sensor.temperature_2"],
"match": "any",
"state": "100",
},
],
}
config = cv.CONDITION_SCHEMA(config)
config = await condition.async_validate_condition_config(hass, config)
test = await condition.async_from_config(hass, config)
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 test(hass)
hass.states.async_set("sensor.temperature_1", 100)
hass.states.async_set("sensor.temperature_2", 101)
assert test(hass)
hass.states.async_set("sensor.temperature_1", 101)
hass.states.async_set("sensor.temperature_2", 101)
assert not test(hass)
async def test_multiple_states(hass):
"""Test with multiple states in condition."""
config = {