mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 15:30:21 +00:00
[core][select] Pass the select.set option as a plain literal (#19568)
This commit is contained in:
@@ -224,6 +224,15 @@ def flash_string(config: ConfigType, value: str) -> str:
|
||||
return str(cg.safe_exp(value))
|
||||
|
||||
|
||||
def literal_with_length(config: ConfigType, value: str) -> str:
|
||||
"""Renderer for a ``(const char *, size_t)`` target: a plain literal plus its byte length.
|
||||
|
||||
The target compares or copies the bytes in place, so it needs the RAM literal rather than
|
||||
the PROGMEM rendering on ESP8266, and the length saves a strlen.
|
||||
"""
|
||||
return f"{cg.safe_exp(value)}, {len(value.encode('utf-8'))}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ApplyCall:
|
||||
"""One statement from config keys, e.g. ``"set_range({}, {})"`` with ``((CONF_LOW, cg.float_), ...)``.
|
||||
|
||||
@@ -494,12 +494,6 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def _literal_with_length(config: ConfigType, value: str) -> str:
|
||||
# The (const char *, size_t) overload compares bytes in place, so it needs a plain
|
||||
# literal rather than the default PROGMEM rendering on ESP8266, and skips a strlen.
|
||||
return f"{cg.safe_exp(value)}, {len(value.encode('utf-8'))}"
|
||||
|
||||
|
||||
automation.register_apply_action(
|
||||
"climate.control",
|
||||
CLIMATE_CONTROL_ACTION_SCHEMA,
|
||||
@@ -517,14 +511,14 @@ automation.register_apply_action(
|
||||
CONF_CUSTOM_FAN_MODE,
|
||||
"set_fan_mode",
|
||||
cg.std_string,
|
||||
const_fn=_literal_with_length,
|
||||
const_fn=automation.literal_with_length,
|
||||
),
|
||||
automation.ApplyField(CONF_PRESET, "set_preset", ClimatePreset),
|
||||
automation.ApplyField(
|
||||
CONF_CUSTOM_PRESET,
|
||||
"set_preset",
|
||||
cg.std_string,
|
||||
const_fn=_literal_with_length,
|
||||
const_fn=automation.literal_with_length,
|
||||
),
|
||||
automation.ApplyField(CONF_SWING_MODE, "set_swing_mode", ClimateSwingMode),
|
||||
call="make_call",
|
||||
|
||||
@@ -142,7 +142,12 @@ automation.register_apply_action(
|
||||
cv.Required(CONF_OPTION): cv.templatable(cv.string_strict),
|
||||
}
|
||||
),
|
||||
automation.ApplyField(CONF_OPTION, "set_option", cg.std_string),
|
||||
automation.ApplyField(
|
||||
CONF_OPTION,
|
||||
"set_option",
|
||||
cg.std_string,
|
||||
const_fn=automation.literal_with_length,
|
||||
),
|
||||
call="make_call",
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from esphome.automation import (
|
||||
TriggerOnTrueForwarder,
|
||||
build_callback_automations,
|
||||
has_non_synchronous_actions,
|
||||
literal_with_length,
|
||||
maybe_simple_id,
|
||||
register_apply_action,
|
||||
register_apply_condition,
|
||||
@@ -838,6 +839,23 @@ async def test_apply_string_constant_stays_in_flash_on_esp8266(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("platform", ["esp32", "esp8266"])
|
||||
async def test_apply_literal_with_length_is_plain_on_every_platform(
|
||||
registries: tuple[Registry, Registry], mock_cg: MockCodegen, platform: str
|
||||
) -> None:
|
||||
"""A (const char *, size_t) target gets the RAM literal and its byte length, never a flash copy."""
|
||||
fields = (
|
||||
ApplyField("option", "set_option", cg.std_string, const_fn=literal_with_length),
|
||||
)
|
||||
await _run_apply_action(
|
||||
registries, fields, {"option": "h\u00e9llo"}, platform=platform
|
||||
)
|
||||
text = _apply_lambda(mock_cg)
|
||||
assert f'::{PARENT_OBJ}->set_option("h\\303\\251llo", 6);' in text
|
||||
assert "progmem_string" not in text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_apply_condition_predicate(
|
||||
registries: tuple[Registry, Registry], mock_cg: MockCodegen
|
||||
|
||||
Reference in New Issue
Block a user