Fix logic of disabled condition for "OR" (#79718)

This commit is contained in:
Karlie Meads 2023-01-16 15:08:09 -05:00 committed by GitHub
parent c6f60bf45d
commit 156307f3f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 11 deletions

View File

@ -870,7 +870,7 @@ async def _async_process_if(
for index, check in enumerate(checks): for index, check in enumerate(checks):
try: try:
with trace_path(["condition", str(index)]): with trace_path(["condition", str(index)]):
if not check(hass, variables): if check(hass, variables) is False:
return False return False
except ConditionError as ex: except ConditionError as ex:
errors.append( errors.append(

View File

@ -10,7 +10,7 @@ import functools as ft
import logging import logging
import re import re
import sys import sys
from typing import Any, cast from typing import Any, Optional, cast
from homeassistant.components import zone as zone_cmp from homeassistant.components import zone as zone_cmp
from homeassistant.components.device_automation import condition as device_condition from homeassistant.components.device_automation import condition as device_condition
@ -80,7 +80,7 @@ INPUT_ENTITY_ID = re.compile(
r"^input_(?:select|text|number|boolean|datetime)\.(?!.+__)(?!_)[\da-z_]+(?<!_)$" r"^input_(?:select|text|number|boolean|datetime)\.(?!.+__)(?!_)[\da-z_]+(?<!_)$"
) )
ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], bool] ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], Optional[bool]]
def condition_trace_append(variables: TemplateVarsType, path: str) -> TraceElement: def condition_trace_append(variables: TemplateVarsType, path: str) -> TraceElement:
@ -139,7 +139,7 @@ def trace_condition_function(condition: ConditionCheckerType) -> ConditionChecke
"""Wrap a condition function to enable basic tracing.""" """Wrap a condition function to enable basic tracing."""
@ft.wraps(condition) @ft.wraps(condition)
def wrapper(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool: def wrapper(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool | None:
"""Trace condition.""" """Trace condition."""
with trace_condition(variables): with trace_condition(variables):
result = condition(hass, variables) result = condition(hass, variables)
@ -173,9 +173,9 @@ async def async_from_config(
@trace_condition_function @trace_condition_function
def disabled_condition( def disabled_condition(
hass: HomeAssistant, variables: TemplateVarsType = None hass: HomeAssistant, variables: TemplateVarsType = None
) -> bool: ) -> bool | None:
"""Condition not enabled, will always pass.""" """Condition not enabled, will act as if it didn't exist."""
return True return None
return disabled_condition return disabled_condition
@ -204,7 +204,7 @@ async def async_and_from_config(
for index, check in enumerate(checks): for index, check in enumerate(checks):
try: try:
with trace_path(["conditions", str(index)]): with trace_path(["conditions", str(index)]):
if not check(hass, variables): if check(hass, variables) is False:
return False return False
except ConditionError as ex: except ConditionError as ex:
errors.append( errors.append(
@ -235,7 +235,7 @@ async def async_or_from_config(
for index, check in enumerate(checks): for index, check in enumerate(checks):
try: try:
with trace_path(["conditions", str(index)]): with trace_path(["conditions", str(index)]):
if check(hass, variables): if check(hass, variables) is True:
return True return True
except ConditionError as ex: except ConditionError as ex:
errors.append( errors.append(

View File

@ -3291,7 +3291,7 @@ async def test_platform_async_validate_condition_config(hass):
async def test_disabled_condition(hass: HomeAssistant) -> None: async def test_disabled_condition(hass: HomeAssistant) -> None:
"""Test a disabled condition always passes.""" """Test a disabled condition returns none."""
config = { config = {
"enabled": False, "enabled": False,
"condition": "state", "condition": "state",
@ -3303,8 +3303,138 @@ async def test_disabled_condition(hass: HomeAssistant) -> None:
test = await condition.async_from_config(hass, config) test = await condition.async_from_config(hass, config)
hass.states.async_set("binary_sensor.test", "on") hass.states.async_set("binary_sensor.test", "on")
assert test(hass) assert test(hass) is None
# Still passses, condition is not enabled # Still passses, condition is not enabled
hass.states.async_set("binary_sensor.test", "off") hass.states.async_set("binary_sensor.test", "off")
assert test(hass) is None
async def test_and_condition_with_disabled_condition(hass):
"""Test the 'and' condition with one of the conditions disabled."""
config = {
"alias": "And Condition",
"condition": "and",
"conditions": [
{
"enabled": False,
"condition": "state",
"entity_id": "sensor.temperature",
"state": "100",
},
{
"condition": "numeric_state",
"entity_id": "sensor.temperature",
"below": 110,
},
],
}
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", 120)
assert not test(hass)
assert_condition_trace(
{
"": [{"result": {"result": False}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": False}}],
"conditions/1/entity_id/0": [
{
"result": {
"result": False,
"wanted_state_below": 110.0,
"state": 120.0,
}
}
],
}
)
hass.states.async_set("sensor.temperature", 105)
assert test(hass) assert test(hass)
assert_condition_trace(
{
"": [{"result": {"result": True}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": True}}],
"conditions/1/entity_id/0": [{"result": {"result": True, "state": 105.0}}],
}
)
hass.states.async_set("sensor.temperature", 100)
assert test(hass)
assert_condition_trace(
{
"": [{"result": {"result": True}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": True}}],
"conditions/1/entity_id/0": [{"result": {"result": True, "state": 100.0}}],
}
)
async def test_or_condition_with_disabled_condition(hass):
"""Test the 'or' condition with one of the conditions disabled."""
config = {
"alias": "Or Condition",
"condition": "or",
"conditions": [
{
"enabled": False,
"condition": "state",
"entity_id": "sensor.temperature",
"state": "100",
},
{
"condition": "numeric_state",
"entity_id": "sensor.temperature",
"below": 110,
},
],
}
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", 120)
assert not test(hass)
assert_condition_trace(
{
"": [{"result": {"result": False}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": False}}],
"conditions/1/entity_id/0": [
{
"result": {
"result": False,
"state": 120.0,
"wanted_state_below": 110.0,
}
}
],
}
)
hass.states.async_set("sensor.temperature", 105)
assert test(hass)
assert_condition_trace(
{
"": [{"result": {"result": True}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": True}}],
"conditions/1/entity_id/0": [{"result": {"result": True, "state": 105.0}}],
}
)
hass.states.async_set("sensor.temperature", 100)
assert test(hass)
assert_condition_trace(
{
"": [{"result": {"result": True}}],
"conditions/0": [{"result": {"result": None}}],
"conditions/1": [{"result": {"result": True}}],
"conditions/1/entity_id/0": [{"result": {"result": True, "state": 100.0}}],
}
)