mirror of
https://github.com/esphome/esphome.git
synced 2025-10-31 23:21:54 +00:00
Merge branch 'dev' into template_value_func_pointers
This commit is contained in:
@@ -16,7 +16,12 @@ from esphome.const import (
|
|||||||
CONF_UPDATE_INTERVAL,
|
CONF_UPDATE_INTERVAL,
|
||||||
)
|
)
|
||||||
from esphome.core import ID
|
from esphome.core import ID
|
||||||
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
|
from esphome.cpp_generator import (
|
||||||
|
LambdaExpression,
|
||||||
|
MockObj,
|
||||||
|
MockObjClass,
|
||||||
|
TemplateArgsType,
|
||||||
|
)
|
||||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||||
from esphome.types import ConfigType
|
from esphome.types import ConfigType
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
@@ -87,6 +92,7 @@ def validate_potentially_or_condition(value):
|
|||||||
|
|
||||||
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
||||||
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
||||||
|
StatelessLambdaAction = cg.esphome_ns.class_("StatelessLambdaAction", Action)
|
||||||
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
||||||
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
|
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
|
||||||
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
|
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
|
||||||
@@ -97,9 +103,40 @@ ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
|
|||||||
Automation = cg.esphome_ns.class_("Automation")
|
Automation = cg.esphome_ns.class_("Automation")
|
||||||
|
|
||||||
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
||||||
|
StatelessLambdaCondition = cg.esphome_ns.class_("StatelessLambdaCondition", Condition)
|
||||||
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
|
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
|
||||||
|
|
||||||
|
|
||||||
|
def new_lambda_pvariable(
|
||||||
|
id_obj: ID,
|
||||||
|
lambda_expr: LambdaExpression,
|
||||||
|
stateless_class: MockObjClass,
|
||||||
|
template_arg: cg.TemplateArguments | None = None,
|
||||||
|
) -> MockObj:
|
||||||
|
"""Create Pvariable for lambda, using stateless class if applicable.
|
||||||
|
|
||||||
|
Combines ID selection and Pvariable creation in one call. For stateless
|
||||||
|
lambdas (empty capture), uses function pointer instead of std::function.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id_obj: The ID object (action_id, condition_id, or filter_id)
|
||||||
|
lambda_expr: The lambda expression object
|
||||||
|
stateless_class: The stateless class to use for stateless lambdas
|
||||||
|
template_arg: Optional template arguments (for actions/conditions)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created Pvariable
|
||||||
|
"""
|
||||||
|
# For stateless lambdas, use function pointer instead of std::function
|
||||||
|
if lambda_expr.capture == "":
|
||||||
|
id_obj = id_obj.copy()
|
||||||
|
id_obj.type = stateless_class
|
||||||
|
|
||||||
|
if template_arg is not None:
|
||||||
|
return cg.new_Pvariable(id_obj, template_arg, lambda_expr)
|
||||||
|
return cg.new_Pvariable(id_obj, lambda_expr)
|
||||||
|
|
||||||
|
|
||||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||||
if extra_schema is None:
|
if extra_schema is None:
|
||||||
extra_schema = {}
|
extra_schema = {}
|
||||||
@@ -240,7 +277,9 @@ async def lambda_condition_to_code(
|
|||||||
args: TemplateArgsType,
|
args: TemplateArgsType,
|
||||||
) -> MockObj:
|
) -> MockObj:
|
||||||
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
||||||
return cg.new_Pvariable(condition_id, template_arg, lambda_)
|
return new_lambda_pvariable(
|
||||||
|
condition_id, lambda_, StatelessLambdaCondition, template_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register_condition(
|
@register_condition(
|
||||||
@@ -406,7 +445,7 @@ async def lambda_action_to_code(
|
|||||||
args: TemplateArgsType,
|
args: TemplateArgsType,
|
||||||
) -> MockObj:
|
) -> MockObj:
|
||||||
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
|
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
|
||||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
return new_lambda_pvariable(action_id, lambda_, StatelessLambdaAction, template_arg)
|
||||||
|
|
||||||
|
|
||||||
@register_action(
|
@register_action(
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Compon
|
|||||||
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
|
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
|
||||||
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
|
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
|
||||||
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
|
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
|
||||||
|
StatelessLambdaFilter = binary_sensor_ns.class_("StatelessLambdaFilter", Filter)
|
||||||
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
|
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
|
||||||
|
|
||||||
_LOGGER = getLogger(__name__)
|
_LOGGER = getLogger(__name__)
|
||||||
@@ -299,7 +300,7 @@ async def lambda_filter_to_code(config, filter_id):
|
|||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
||||||
)
|
)
|
||||||
return cg.new_Pvariable(filter_id, lambda_)
|
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
|
||||||
|
|
||||||
|
|
||||||
@register_filter(
|
@register_filter(
|
||||||
|
|||||||
@@ -111,6 +111,21 @@ class LambdaFilter : public Filter {
|
|||||||
std::function<optional<bool>(bool)> f_;
|
std::function<optional<bool>(bool)> f_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Optimized lambda filter for stateless lambdas (no capture).
|
||||||
|
*
|
||||||
|
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||||
|
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||||
|
*/
|
||||||
|
class StatelessLambdaFilter : public Filter {
|
||||||
|
public:
|
||||||
|
explicit StatelessLambdaFilter(optional<bool> (*f)(bool)) : f_(f) {}
|
||||||
|
|
||||||
|
optional<bool> new_value(bool value) override { return this->f_(value); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
optional<bool> (*f_)(bool);
|
||||||
|
};
|
||||||
|
|
||||||
class SettleFilter : public Filter, public Component {
|
class SettleFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
optional<bool> new_value(bool value) override;
|
optional<bool> new_value(bool value) override;
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ class ESP32InternalGPIOPin : public InternalGPIOPin {
|
|||||||
// - 3 bytes for members below
|
// - 3 bytes for members below
|
||||||
// - 1 byte padding for alignment
|
// - 1 byte padding for alignment
|
||||||
// - 4 bytes for vtable pointer
|
// - 4 bytes for vtable pointer
|
||||||
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
|
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
|
||||||
gpio::Flags flags_; // GPIO flags (1 byte)
|
gpio::Flags flags_{}; // GPIO flags (1 byte)
|
||||||
struct PinFlags {
|
struct PinFlags {
|
||||||
uint8_t inverted : 1; // Invert pin logic (1 bit)
|
uint8_t inverted : 1; // Invert pin logic (1 bit)
|
||||||
uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits)
|
uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits)
|
||||||
uint8_t reserved : 5; // Reserved for future use (5 bits)
|
uint8_t reserved : 5; // Reserved for future use (5 bits)
|
||||||
} pin_flags_; // Total: 1 byte
|
} pin_flags_{}; // Total: 1 byte
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
static bool isr_service_installed;
|
static bool isr_service_installed;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -223,7 +223,10 @@ async def esp32_pin_to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
|
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
if CONF_DRIVE_STRENGTH in config:
|
if CONF_DRIVE_STRENGTH in config:
|
||||||
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
|
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ class ESP8266GPIOPin : public InternalGPIOPin {
|
|||||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||||
|
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esp8266
|
} // namespace esp8266
|
||||||
|
|||||||
@@ -165,7 +165,10 @@ async def esp8266_pin_to_code(config):
|
|||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
mode = config[CONF_MODE]
|
mode = config[CONF_MODE]
|
||||||
cg.add(var.set_pin(num))
|
cg.add(var.set_pin(num))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(mode)))
|
cg.add(var.set_flags(pins.gpio_flags_expr(mode)))
|
||||||
if num < 16:
|
if num < 16:
|
||||||
initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][
|
initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class HostGPIOPin : public InternalGPIOPin {
|
|||||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||||
|
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace host
|
} // namespace host
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ async def host_pin_to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
cg.add(var.set_pin(num))
|
cg.add(var.set_pin(num))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
return var
|
return var
|
||||||
|
|||||||
@@ -199,6 +199,9 @@ async def component_pin_to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
cg.add(var.set_pin(num))
|
cg.add(var.set_pin(num))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
return var
|
return var
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin {
|
|||||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||||
|
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace libretiny
|
} // namespace libretiny
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import LambdaAction
|
from esphome.automation import LambdaAction, StatelessLambdaAction
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
||||||
from esphome.components.esp32.const import (
|
from esphome.components.esp32.const import (
|
||||||
@@ -430,7 +430,9 @@ async def logger_log_action_to_code(config, action_id, template_arg, args):
|
|||||||
text = str(cg.statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args_)))
|
text = str(cg.statement(esp_log(config[CONF_TAG], config[CONF_FORMAT], *args_)))
|
||||||
|
|
||||||
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
||||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
return automation.new_lambda_pvariable(
|
||||||
|
action_id, lambda_, StatelessLambdaAction, template_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
@@ -455,7 +457,9 @@ async def logger_set_level_to_code(config, action_id, template_arg, args):
|
|||||||
text = str(cg.statement(logger.set_log_level(level)))
|
text = str(cg.statement(logger.set_log_level(level)))
|
||||||
|
|
||||||
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
||||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
return automation.new_lambda_pvariable(
|
||||||
|
action_id, lambda_, StatelessLambdaAction, template_arg
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ async def nrf52_pin_to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
cg.add(var.set_pin(num))
|
cg.add(var.set_pin(num))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
return var
|
return var
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ class RP2040GPIOPin : public InternalGPIOPin {
|
|||||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||||
|
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rp2040
|
} // namespace rp2040
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ async def rp2040_pin_to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
num = config[CONF_NUMBER]
|
num = config[CONF_NUMBER]
|
||||||
cg.add(var.set_pin(num))
|
cg.add(var.set_pin(num))
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
# Only set if true to avoid bloating setup() function
|
||||||
|
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
|
||||||
|
if config[CONF_INVERTED]:
|
||||||
|
cg.add(var.set_inverted(True))
|
||||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||||
return var
|
return var
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ ExponentialMovingAverageFilter = sensor_ns.class_(
|
|||||||
)
|
)
|
||||||
ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Component)
|
ThrottleAverageFilter = sensor_ns.class_("ThrottleAverageFilter", Filter, cg.Component)
|
||||||
LambdaFilter = sensor_ns.class_("LambdaFilter", Filter)
|
LambdaFilter = sensor_ns.class_("LambdaFilter", Filter)
|
||||||
|
StatelessLambdaFilter = sensor_ns.class_("StatelessLambdaFilter", Filter)
|
||||||
OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
|
OffsetFilter = sensor_ns.class_("OffsetFilter", Filter)
|
||||||
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
|
MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter)
|
||||||
ValueListFilter = sensor_ns.class_("ValueListFilter", Filter)
|
ValueListFilter = sensor_ns.class_("ValueListFilter", Filter)
|
||||||
@@ -573,7 +574,7 @@ async def lambda_filter_to_code(config, filter_id):
|
|||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config, [(float, "x")], return_type=cg.optional.template(float)
|
config, [(float, "x")], return_type=cg.optional.template(float)
|
||||||
)
|
)
|
||||||
return cg.new_Pvariable(filter_id, lambda_)
|
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
|
||||||
|
|
||||||
|
|
||||||
DELTA_SCHEMA = cv.Schema(
|
DELTA_SCHEMA = cv.Schema(
|
||||||
|
|||||||
@@ -296,6 +296,21 @@ class LambdaFilter : public Filter {
|
|||||||
lambda_filter_t lambda_filter_;
|
lambda_filter_t lambda_filter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Optimized lambda filter for stateless lambdas (no capture).
|
||||||
|
*
|
||||||
|
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||||
|
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||||
|
*/
|
||||||
|
class StatelessLambdaFilter : public Filter {
|
||||||
|
public:
|
||||||
|
explicit StatelessLambdaFilter(optional<float> (*lambda_filter)(float)) : lambda_filter_(lambda_filter) {}
|
||||||
|
|
||||||
|
optional<float> new_value(float value) override { return this->lambda_filter_(value); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
optional<float> (*lambda_filter_)(float);
|
||||||
|
};
|
||||||
|
|
||||||
/// A simple filter that adds `offset` to each value it receives.
|
/// A simple filter that adds `offset` to each value it receives.
|
||||||
class OffsetFilter : public Filter {
|
class OffsetFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
|
|||||||
# Filters
|
# Filters
|
||||||
Filter = text_sensor_ns.class_("Filter")
|
Filter = text_sensor_ns.class_("Filter")
|
||||||
LambdaFilter = text_sensor_ns.class_("LambdaFilter", Filter)
|
LambdaFilter = text_sensor_ns.class_("LambdaFilter", Filter)
|
||||||
|
StatelessLambdaFilter = text_sensor_ns.class_("StatelessLambdaFilter", Filter)
|
||||||
ToUpperFilter = text_sensor_ns.class_("ToUpperFilter", Filter)
|
ToUpperFilter = text_sensor_ns.class_("ToUpperFilter", Filter)
|
||||||
ToLowerFilter = text_sensor_ns.class_("ToLowerFilter", Filter)
|
ToLowerFilter = text_sensor_ns.class_("ToLowerFilter", Filter)
|
||||||
AppendFilter = text_sensor_ns.class_("AppendFilter", Filter)
|
AppendFilter = text_sensor_ns.class_("AppendFilter", Filter)
|
||||||
@@ -70,7 +71,7 @@ async def lambda_filter_to_code(config, filter_id):
|
|||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config, [(cg.std_string, "x")], return_type=cg.optional.template(cg.std_string)
|
config, [(cg.std_string, "x")], return_type=cg.optional.template(cg.std_string)
|
||||||
)
|
)
|
||||||
return cg.new_Pvariable(filter_id, lambda_)
|
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register("to_upper", ToUpperFilter, {})
|
@FILTER_REGISTRY.register("to_upper", ToUpperFilter, {})
|
||||||
|
|||||||
@@ -62,6 +62,21 @@ class LambdaFilter : public Filter {
|
|||||||
lambda_filter_t lambda_filter_;
|
lambda_filter_t lambda_filter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Optimized lambda filter for stateless lambdas (no capture).
|
||||||
|
*
|
||||||
|
* Uses function pointer instead of std::function to reduce memory overhead.
|
||||||
|
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||||
|
*/
|
||||||
|
class StatelessLambdaFilter : public Filter {
|
||||||
|
public:
|
||||||
|
explicit StatelessLambdaFilter(optional<std::string> (*lambda_filter)(std::string)) : lambda_filter_(lambda_filter) {}
|
||||||
|
|
||||||
|
optional<std::string> new_value(std::string value) override { return this->lambda_filter_(value); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
optional<std::string> (*lambda_filter_)(std::string);
|
||||||
|
};
|
||||||
|
|
||||||
/// A simple filter that converts all text to uppercase
|
/// A simple filter that converts all text to uppercase
|
||||||
class ToUpperFilter : public Filter {
|
class ToUpperFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ class ZephyrGPIOPin : public InternalGPIOPin {
|
|||||||
protected:
|
protected:
|
||||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
bool inverted_;
|
bool inverted_{};
|
||||||
gpio::Flags flags_;
|
gpio::Flags flags_{};
|
||||||
const device *gpio_ = nullptr;
|
const device *gpio_{nullptr};
|
||||||
bool value_ = false;
|
bool value_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zephyr
|
} // namespace zephyr
|
||||||
|
|||||||
@@ -79,6 +79,18 @@ template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
|
|||||||
std::function<bool(Ts...)> f_;
|
std::function<bool(Ts...)> f_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Optimized lambda condition for stateless lambdas (no capture).
|
||||||
|
/// Uses function pointer instead of std::function to reduce memory overhead.
|
||||||
|
/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||||
|
template<typename... Ts> class StatelessLambdaCondition : public Condition<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit StatelessLambdaCondition(bool (*f)(Ts...)) : f_(f) {}
|
||||||
|
bool check(Ts... x) override { return this->f_(x...); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool (*f_)(Ts...);
|
||||||
|
};
|
||||||
|
|
||||||
template<typename... Ts> class ForCondition : public Condition<Ts...>, public Component {
|
template<typename... Ts> class ForCondition : public Condition<Ts...>, public Component {
|
||||||
public:
|
public:
|
||||||
explicit ForCondition(Condition<> *condition) : condition_(condition) {}
|
explicit ForCondition(Condition<> *condition) : condition_(condition) {}
|
||||||
@@ -190,6 +202,19 @@ template<typename... Ts> class LambdaAction : public Action<Ts...> {
|
|||||||
std::function<void(Ts...)> f_;
|
std::function<void(Ts...)> f_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Optimized lambda action for stateless lambdas (no capture).
|
||||||
|
/// Uses function pointer instead of std::function to reduce memory overhead.
|
||||||
|
/// Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
|
||||||
|
template<typename... Ts> class StatelessLambdaAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->f_(x...); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void (*f_)(Ts...);
|
||||||
|
};
|
||||||
|
|
||||||
template<typename... Ts> class IfAction : public Action<Ts...> {
|
template<typename... Ts> class IfAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
|
explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Topic :: Home Automation",
|
"Topic :: Home Automation",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.11.0"
|
|
||||||
|
# Python 3.14 is currently not supported by IDF <= 5.5.1, see https://github.com/esphome/esphome/issues/11502
|
||||||
|
requires-python = ">=3.11.0,<3.14"
|
||||||
|
|
||||||
dynamic = ["dependencies", "optional-dependencies", "version"]
|
dynamic = ["dependencies", "optional-dependencies", "version"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user