mirror of
https://github.com/esphome/esphome.git
synced 2026-10-20 13:06:45 +00:00
Fill the in_range bounds at code generation so the validated config is unchanged
This commit is contained in:
+10
-7
@@ -355,11 +355,6 @@ class ApplyField:
|
||||
return ApplyCall(target, ((self.conf_key, self.type_, self.const_fn),))
|
||||
|
||||
|
||||
def fill_in_range_bounds(config: ConfigType) -> ConfigType:
|
||||
"""Give an absent ``above`` or ``below`` the value NaN, which ``IN_RANGE_CHECK`` renders."""
|
||||
return {CONF_ABOVE: math.nan, CONF_BELOW: math.nan, **config}
|
||||
|
||||
|
||||
def _in_range_low(config: ConfigType, value: float) -> str:
|
||||
return "-INFINITY" if math.isnan(value) else str(cg.safe_exp(value))
|
||||
|
||||
@@ -371,11 +366,12 @@ def _in_range_high(config: ConfigType, value: float) -> str:
|
||||
return str(cg.safe_exp(value))
|
||||
|
||||
|
||||
# ``above <= state <= below`` on the parent's float ``state``, after ``fill_in_range_bounds``.
|
||||
# ``above <= state <= below`` on the parent's float ``state``; register with ``IN_RANGE_DEFAULTS``.
|
||||
IN_RANGE_CHECK = ApplyCall(
|
||||
"state >= {} && {parent}->state <= {}",
|
||||
((CONF_ABOVE, cg.float_, _in_range_low), (CONF_BELOW, cg.float_, _in_range_high)),
|
||||
)
|
||||
IN_RANGE_DEFAULTS = {CONF_ABOVE: math.nan, CONF_BELOW: math.nan}
|
||||
|
||||
|
||||
def _config_lookup(config: ConfigType, key: str | tuple[str, ...]) -> Any:
|
||||
@@ -596,7 +592,11 @@ async def _render_check(
|
||||
|
||||
|
||||
def register_apply_condition(
|
||||
name: str, schema: cv.Schema, check: str | ApplyCall, id_key: str = CONF_ID
|
||||
name: str,
|
||||
schema: cv.Schema,
|
||||
check: str | ApplyCall,
|
||||
id_key: str = CONF_ID,
|
||||
defaults: ConfigType | None = None,
|
||||
) -> None:
|
||||
"""Register a condition that is one expression on its parent, with no C++ class.
|
||||
|
||||
@@ -605,6 +605,7 @@ def register_apply_condition(
|
||||
against config values, all of which must be present. Write ``== false`` to negate.
|
||||
A ``cg.std_string`` constant stays in flash on ESP8266 and supports only ``==`` or ``!=``
|
||||
against a ``std::string`` or ``StringRef`` member; other string types are plain literals.
|
||||
``defaults`` fills absent keys at code generation only, so the validated config is unchanged.
|
||||
Generates one static predicate and an ``ApplyCondition`` templated on it.
|
||||
"""
|
||||
call = check if isinstance(check, ApplyCall) else ApplyCall(check)
|
||||
@@ -619,6 +620,8 @@ def register_apply_condition(
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
if defaults:
|
||||
config = {**defaults, **config}
|
||||
parent = await _apply_parent(config, id_key)
|
||||
lambda_args = _apply_lambda_args(args)
|
||||
values = _apply_values(config, members)
|
||||
|
||||
@@ -166,8 +166,6 @@ ValueRangeTrigger = number_ns.class_(
|
||||
"ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component
|
||||
)
|
||||
|
||||
# Conditions
|
||||
|
||||
NumberMode = number_ns.enum("NumberMode")
|
||||
|
||||
# Schema default that also matches the C++ initializer in number_traits.h; codegen
|
||||
@@ -339,12 +337,14 @@ NUMBER_IN_RANGE_CONDITION_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_BELOW): cv.float_,
|
||||
},
|
||||
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
||||
automation.fill_in_range_bounds,
|
||||
)
|
||||
|
||||
|
||||
automation.register_apply_condition(
|
||||
"number.in_range", NUMBER_IN_RANGE_CONDITION_SCHEMA, automation.IN_RANGE_CHECK
|
||||
"number.in_range",
|
||||
NUMBER_IN_RANGE_CONDITION_SCHEMA,
|
||||
automation.IN_RANGE_CHECK,
|
||||
defaults=automation.IN_RANGE_DEFAULTS,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1106,12 +1106,14 @@ SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_BELOW): cv.float_,
|
||||
},
|
||||
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
|
||||
automation.fill_in_range_bounds,
|
||||
)
|
||||
|
||||
|
||||
automation.register_apply_condition(
|
||||
"sensor.in_range", SENSOR_IN_RANGE_CONDITION_SCHEMA, automation.IN_RANGE_CHECK
|
||||
"sensor.in_range",
|
||||
SENSOR_IN_RANGE_CONDITION_SCHEMA,
|
||||
automation.IN_RANGE_CHECK,
|
||||
defaults=automation.IN_RANGE_DEFAULTS,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import pytest
|
||||
|
||||
from esphome.automation import (
|
||||
IN_RANGE_CHECK,
|
||||
IN_RANGE_DEFAULTS,
|
||||
ApplyAction,
|
||||
ApplyCall,
|
||||
ApplyCondition,
|
||||
@@ -26,7 +27,6 @@ from esphome.automation import (
|
||||
build_parent_callback_automation,
|
||||
build_trigger_automations,
|
||||
build_trigger_callback,
|
||||
fill_in_range_bounds,
|
||||
has_non_synchronous_actions,
|
||||
literal_with_length,
|
||||
maybe_simple_id,
|
||||
@@ -1613,7 +1613,7 @@ async def test_nested_repeat_lambda_parameters_are_distinct() -> None:
|
||||
|
||||
|
||||
def _render_in_range(config: dict) -> tuple[str, str]:
|
||||
config = fill_in_range_bounds(config)
|
||||
config = {**IN_RANGE_DEFAULTS, **config}
|
||||
(above_key, _, low), (below_key, _, high) = IN_RANGE_CHECK.members
|
||||
return low(config, config[above_key]), high(config, config[below_key])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user