mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Allow any entity to match state condition (#69763)
This commit is contained in:
parent
85f698f873
commit
7087020283
@ -61,6 +61,7 @@ MATCH_ALL: Final = "*"
|
|||||||
# Entity target all constant
|
# Entity target all constant
|
||||||
ENTITY_MATCH_NONE: Final = "none"
|
ENTITY_MATCH_NONE: Final = "none"
|
||||||
ENTITY_MATCH_ALL: Final = "all"
|
ENTITY_MATCH_ALL: Final = "all"
|
||||||
|
ENTITY_MATCH_ANY: Final = "any"
|
||||||
|
|
||||||
# If no name is specified
|
# If no name is specified
|
||||||
DEVICE_DEFAULT_NAME: Final = "Unnamed Device"
|
DEVICE_DEFAULT_NAME: Final = "Unnamed Device"
|
||||||
@ -172,6 +173,7 @@ CONF_LIGHTS: Final = "lights"
|
|||||||
CONF_LOCATION: Final = "location"
|
CONF_LOCATION: Final = "location"
|
||||||
CONF_LONGITUDE: Final = "longitude"
|
CONF_LONGITUDE: Final = "longitude"
|
||||||
CONF_MAC: Final = "mac"
|
CONF_MAC: Final = "mac"
|
||||||
|
CONF_MATCH: Final = "match"
|
||||||
CONF_MAXIMUM: Final = "maximum"
|
CONF_MAXIMUM: Final = "maximum"
|
||||||
CONF_MEDIA_DIRS: Final = "media_dirs"
|
CONF_MEDIA_DIRS: Final = "media_dirs"
|
||||||
CONF_METHOD: Final = "method"
|
CONF_METHOD: Final = "method"
|
||||||
|
@ -29,10 +29,13 @@ from homeassistant.const import (
|
|||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_ENTITY_ID,
|
CONF_ENTITY_ID,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MATCH,
|
||||||
CONF_STATE,
|
CONF_STATE,
|
||||||
CONF_VALUE_TEMPLATE,
|
CONF_VALUE_TEMPLATE,
|
||||||
CONF_WEEKDAY,
|
CONF_WEEKDAY,
|
||||||
CONF_ZONE,
|
CONF_ZONE,
|
||||||
|
ENTITY_MATCH_ALL,
|
||||||
|
ENTITY_MATCH_ANY,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
SUN_EVENT_SUNRISE,
|
SUN_EVENT_SUNRISE,
|
||||||
@ -524,6 +527,7 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
|
|||||||
req_states: str | list[str] = config.get(CONF_STATE, [])
|
req_states: str | list[str] = config.get(CONF_STATE, [])
|
||||||
for_period = config.get("for")
|
for_period = config.get("for")
|
||||||
attribute = config.get(CONF_ATTRIBUTE)
|
attribute = config.get(CONF_ATTRIBUTE)
|
||||||
|
match = config.get(CONF_MATCH, ENTITY_MATCH_ALL)
|
||||||
|
|
||||||
if not isinstance(req_states, list):
|
if not isinstance(req_states, list):
|
||||||
req_states = [req_states]
|
req_states = [req_states]
|
||||||
@ -532,10 +536,13 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
|
|||||||
def if_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
def if_state(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
|
||||||
"""Test if condition."""
|
"""Test if condition."""
|
||||||
errors = []
|
errors = []
|
||||||
|
result: bool = match != ENTITY_MATCH_ANY
|
||||||
for index, entity_id in enumerate(entity_ids):
|
for index, entity_id in enumerate(entity_ids):
|
||||||
try:
|
try:
|
||||||
with trace_path(["entity_id", str(index)]), trace_condition(variables):
|
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
|
return False
|
||||||
except ConditionError as ex:
|
except ConditionError as ex:
|
||||||
errors.append(
|
errors.append(
|
||||||
@ -548,7 +555,7 @@ def state_from_config(config: ConfigType) -> ConditionCheckerType:
|
|||||||
if errors:
|
if errors:
|
||||||
raise ConditionErrorContainer("state", errors=errors)
|
raise ConditionErrorContainer("state", errors=errors)
|
||||||
|
|
||||||
return True
|
return result
|
||||||
|
|
||||||
return if_state
|
return if_state
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ from homeassistant.const import (
|
|||||||
CONF_EVENT_DATA_TEMPLATE,
|
CONF_EVENT_DATA_TEMPLATE,
|
||||||
CONF_FOR,
|
CONF_FOR,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MATCH,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_REPEAT,
|
CONF_REPEAT,
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
@ -68,6 +69,7 @@ from homeassistant.const import (
|
|||||||
CONF_WAIT_TEMPLATE,
|
CONF_WAIT_TEMPLATE,
|
||||||
CONF_WHILE,
|
CONF_WHILE,
|
||||||
ENTITY_MATCH_ALL,
|
ENTITY_MATCH_ALL,
|
||||||
|
ENTITY_MATCH_ANY,
|
||||||
ENTITY_MATCH_NONE,
|
ENTITY_MATCH_NONE,
|
||||||
SUN_EVENT_SUNRISE,
|
SUN_EVENT_SUNRISE,
|
||||||
SUN_EVENT_SUNSET,
|
SUN_EVENT_SUNSET,
|
||||||
@ -1105,6 +1107,9 @@ STATE_CONDITION_BASE_SCHEMA = {
|
|||||||
**CONDITION_BASE_SCHEMA,
|
**CONDITION_BASE_SCHEMA,
|
||||||
vol.Required(CONF_CONDITION): "state",
|
vol.Required(CONF_CONDITION): "state",
|
||||||
vol.Required(CONF_ENTITY_ID): entity_ids_or_uuids,
|
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_ATTRIBUTE): str,
|
||||||
vol.Optional(CONF_FOR): positive_time_period,
|
vol.Optional(CONF_FOR): positive_time_period,
|
||||||
# To support use_trigger_value in automation
|
# To support use_trigger_value in automation
|
||||||
|
@ -15,6 +15,7 @@ from homeassistant.const import (
|
|||||||
SUN_EVENT_SUNRISE,
|
SUN_EVENT_SUNRISE,
|
||||||
SUN_EVENT_SUNSET,
|
SUN_EVENT_SUNSET,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConditionError, HomeAssistantError
|
from homeassistant.exceptions import ConditionError, HomeAssistantError
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
condition,
|
condition,
|
||||||
@ -1020,6 +1021,40 @@ async def test_state_multiple_entities(hass):
|
|||||||
assert not test(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):
|
async def test_multiple_states(hass):
|
||||||
"""Test with multiple states in condition."""
|
"""Test with multiple states in condition."""
|
||||||
config = {
|
config = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user