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):
try:
with trace_path(["condition", str(index)]):
if not check(hass, variables):
if check(hass, variables) is False:
return False
except ConditionError as ex:
errors.append(

View File

@ -10,7 +10,7 @@ import functools as ft
import logging
import re
import sys
from typing import Any, cast
from typing import Any, Optional, cast
from homeassistant.components import zone as zone_cmp
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_]+(?<!_)$"
)
ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], bool]
ConditionCheckerType = Callable[[HomeAssistant, TemplateVarsType], Optional[bool]]
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."""
@ft.wraps(condition)
def wrapper(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool:
def wrapper(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool | None:
"""Trace condition."""
with trace_condition(variables):
result = condition(hass, variables)
@ -173,9 +173,9 @@ async def async_from_config(
@trace_condition_function
def disabled_condition(
hass: HomeAssistant, variables: TemplateVarsType = None
) -> bool:
"""Condition not enabled, will always pass."""
return True
) -> bool | None:
"""Condition not enabled, will act as if it didn't exist."""
return None
return disabled_condition
@ -204,7 +204,7 @@ async def async_and_from_config(
for index, check in enumerate(checks):
try:
with trace_path(["conditions", str(index)]):
if not check(hass, variables):
if check(hass, variables) is False:
return False
except ConditionError as ex:
errors.append(
@ -235,7 +235,7 @@ async def async_or_from_config(
for index, check in enumerate(checks):
try:
with trace_path(["conditions", str(index)]):
if check(hass, variables):
if check(hass, variables) is True:
return True
except ConditionError as ex:
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:
"""Test a disabled condition always passes."""
"""Test a disabled condition returns none."""
config = {
"enabled": False,
"condition": "state",
@ -3303,8 +3303,138 @@ async def test_disabled_condition(hass: HomeAssistant) -> None:
test = await condition.async_from_config(hass, config)
hass.states.async_set("binary_sensor.test", "on")
assert test(hass)
assert test(hass) is None
# Still passses, condition is not enabled
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_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}}],
}
)