mirror of
https://github.com/esphome/esphome.git
synced 2026-02-24 20:35:30 -07:00
light effect to uint32_t
This commit is contained in:
@@ -41,7 +41,7 @@ template<typename... Ts> class LightControlAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(float, color_temperature)
|
||||
TEMPLATABLE_VALUE(float, cold_white)
|
||||
TEMPLATABLE_VALUE(float, warm_white)
|
||||
TEMPLATABLE_VALUE(std::string, effect)
|
||||
TEMPLATABLE_VALUE(uint32_t, effect)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->parent_->make_call();
|
||||
|
||||
@@ -10,12 +10,14 @@ from esphome.const import (
|
||||
CONF_COLOR_MODE,
|
||||
CONF_COLOR_TEMPERATURE,
|
||||
CONF_EFFECT,
|
||||
CONF_EFFECTS,
|
||||
CONF_FLASH_LENGTH,
|
||||
CONF_GREEN,
|
||||
CONF_ID,
|
||||
CONF_LIMIT_MODE,
|
||||
CONF_MAX_BRIGHTNESS,
|
||||
CONF_MIN_BRIGHTNESS,
|
||||
CONF_NAME,
|
||||
CONF_RANGE_FROM,
|
||||
CONF_RANGE_TO,
|
||||
CONF_RED,
|
||||
@@ -24,6 +26,8 @@ from esphome.const import (
|
||||
CONF_WARM_WHITE,
|
||||
CONF_WHITE,
|
||||
)
|
||||
from esphome.core import CORE, Lambda
|
||||
from esphome.cpp_generator import LambdaExpression
|
||||
|
||||
from .types import (
|
||||
COLOR_MODES,
|
||||
@@ -110,6 +114,25 @@ LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||
)
|
||||
|
||||
|
||||
def _resolve_effect_index(config):
|
||||
"""Resolve a static effect name to its 1-based index at codegen time.
|
||||
|
||||
Effect index 0 means "None" (no effect). Effects are 1-indexed matching
|
||||
the C++ convention in LightState.
|
||||
"""
|
||||
effect_name = config[CONF_EFFECT]
|
||||
if effect_name.lower() == "none":
|
||||
return 0
|
||||
light_id = config[CONF_ID]
|
||||
light_path = CORE.config.get_path_for_id(light_id)[:-1]
|
||||
light_config = CORE.config.get_config_for_path(light_path)
|
||||
for i, effect_conf in enumerate(light_config.get(CONF_EFFECTS, [])):
|
||||
key = next(iter(effect_conf))
|
||||
if effect_conf[key][CONF_NAME].lower() == effect_name.lower():
|
||||
return i + 1
|
||||
raise ValueError(f"Effect '{effect_name}' not found in light '{light_id}'")
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"light.turn_off", LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA
|
||||
)
|
||||
@@ -164,8 +187,26 @@ async def light_control_to_code(config, action_id, template_arg, args):
|
||||
template_ = await cg.templatable(config[CONF_WARM_WHITE], args, float)
|
||||
cg.add(var.set_warm_white(template_))
|
||||
if CONF_EFFECT in config:
|
||||
template_ = await cg.templatable(config[CONF_EFFECT], args, cg.std_string)
|
||||
cg.add(var.set_effect(template_))
|
||||
if isinstance(config[CONF_EFFECT], Lambda):
|
||||
# Lambda returns a string — wrap in a C++ lambda that resolves
|
||||
# the effect name to its uint32_t index at runtime
|
||||
inner_lambda = await cg.process_lambda(
|
||||
config[CONF_EFFECT], args, return_type=cg.std_string
|
||||
)
|
||||
fwd_args = ", ".join(n for _, n in args)
|
||||
wrapper = LambdaExpression(
|
||||
f"auto __effect_s = ({inner_lambda})({fwd_args});\n"
|
||||
f"return {paren}->get_effect_index("
|
||||
f"__effect_s.c_str(), __effect_s.size());",
|
||||
args,
|
||||
capture="",
|
||||
return_type=cg.uint32,
|
||||
)
|
||||
cg.add(var.set_effect(wrapper))
|
||||
else:
|
||||
# Static string — resolve effect name to index at codegen time
|
||||
effect_index = _resolve_effect_index(config)
|
||||
cg.add(var.set_effect(effect_index))
|
||||
return var
|
||||
|
||||
|
||||
|
||||
@@ -200,6 +200,20 @@ class LightState : public EntityBase, public Component {
|
||||
return 0; // Effect not found
|
||||
}
|
||||
|
||||
/// Get effect index by name (const char* overload, avoids std::string construction).
|
||||
uint32_t get_effect_index(const char *name, size_t len) const {
|
||||
StringRef ref(name, len);
|
||||
if (str_equals_case_insensitive(ref, StringRef("none", 4))) {
|
||||
return 0;
|
||||
}
|
||||
for (size_t i = 0; i < this->effects_.size(); i++) {
|
||||
if (str_equals_case_insensitive(ref, this->effects_[i]->get_name())) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Get effect by index. Returns nullptr if index is invalid.
|
||||
LightEffect *get_effect_by_index(uint32_t index) const {
|
||||
if (index == 0 || index > this->effects_.size()) {
|
||||
|
||||
@@ -71,6 +71,32 @@ esphome:
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
state: on
|
||||
# Test static effect name resolution at codegen time
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: Strobe
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: none
|
||||
# Test resolving a different effect on the same light
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: My Flicker
|
||||
# Test effect: None (capitalized)
|
||||
- light.control:
|
||||
id: test_monochromatic_light
|
||||
effect: None
|
||||
# Test effect lambda with no args (on_boot has empty Ts...)
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return "Strobe";'
|
||||
# Test effect lambda with non-empty args (repeat passes uint32_t iteration)
|
||||
- repeat:
|
||||
count: 3
|
||||
then:
|
||||
- light.turn_on:
|
||||
id: test_monochromatic_light
|
||||
effect: !lambda 'return iteration > 1 ? "Strobe" : "none";'
|
||||
- light.dim_relative:
|
||||
id: test_monochromatic_light
|
||||
relative_brightness: 5%
|
||||
|
||||
Reference in New Issue
Block a user