1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-05 09:31:54 +00:00

Compare commits

..

11 Commits

Author SHA1 Message Date
J. Nick Koston
25147920a5 Merge branch 'memory_api' into memory_api_action_chaining_const_ref 2025-11-04 08:34:51 -06:00
J. Nick Koston
67c57aa5ed Merge branch 'memory_api' into memory_api_action_chaining_const_ref 2025-11-03 22:43:37 -06:00
J. Nick Koston
ba2e8759e5 Merge branch 'action_chaining_const_ref' into memory_api_action_chaining_const_ref 2025-11-03 21:04:56 -06:00
J. Nick Koston
9a9fecfedf missed some 2025-11-03 19:29:35 -06:00
J. Nick Koston
0fc7912171 missed some 2025-11-03 19:28:07 -06:00
J. Nick Koston
3a191e571b fix new code 2025-11-03 18:37:03 -06:00
J. Nick Koston
025e9d4ae2 remove some now useless moves 2025-11-03 18:08:36 -06:00
J. Nick Koston
4ecab07562 [core] Reduce action framework argument copies by 83% 2025-11-03 17:49:11 -06:00
J. Nick Koston
38fe47b554 Merge branch 'dev' into action_chaining 2025-11-03 17:45:59 -06:00
J. Nick Koston
738cf4cace Merge branch 'dev' into action_chaining 2025-11-02 19:09:44 -06:00
Jesse Hills
0a01fde503 Merge branch 'dev' into action_chaining 2025-11-03 14:07:15 +13:00
41 changed files with 103 additions and 309 deletions

View File

@@ -181,7 +181,7 @@ esphome/components/gdk101/* @Szewcson
esphome/components/gl_r01_i2c/* @pkejval
esphome/components/globals/* @esphome/core
esphome/components/gp2y1010au0f/* @zry98
esphome/components/gp8403/* @jesserockz @sebydocky
esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core
esphome/components/gpio/one_wire/* @ssieb
esphome/components/gps/* @coogle @ximex

View File

@@ -8,7 +8,6 @@ BYTE_ORDER_BIG = "big_endian"
CONF_COLOR_DEPTH = "color_depth"
CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ENABLED = "enabled"
CONF_ON_RECEIVE = "on_receive"
CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers"

View File

@@ -176,117 +176,7 @@ class Display;
class DisplayPage;
class DisplayOnPageChangeTrigger;
/** Optimized display writer that uses function pointers for stateless lambdas.
*
* Similar to TemplatableValue but specialized for display writer callbacks.
* Saves ~8 bytes per stateless lambda on 32-bit platforms (16 bytes std::function → ~8 bytes discriminator+pointer).
*
* Supports both:
* - Stateless lambdas (from YAML) → function pointer (4 bytes)
* - Stateful lambdas/std::function (from C++ code) → std::function* (heap allocated)
*
* @tparam T The display type (e.g., Display, Nextion, GPIOLCDDisplay)
*/
template<typename T> class DisplayWriter {
public:
DisplayWriter() : type_(NONE) {}
// For stateless lambdas (convertible to function pointer): use function pointer (4 bytes)
template<typename F>
DisplayWriter(F f) requires std::invocable<F, T &> && std::convertible_to<F, void (*)(T &)>
: type_(STATELESS_LAMBDA) {
this->stateless_f_ = f; // Implicit conversion to function pointer
}
// For stateful lambdas and std::function (not convertible to function pointer): use std::function* (heap allocated)
// This handles backwards compatibility with external components
template<typename F>
DisplayWriter(F f) requires std::invocable<F, T &> &&(!std::convertible_to<F, void (*)(T &)>) : type_(LAMBDA) {
this->f_ = new std::function<void(T &)>(std::move(f));
}
// Copy constructor
DisplayWriter(const DisplayWriter &other) : type_(other.type_) {
if (type_ == LAMBDA) {
this->f_ = new std::function<void(T &)>(*other.f_);
} else if (type_ == STATELESS_LAMBDA) {
this->stateless_f_ = other.stateless_f_;
}
}
// Move constructor
DisplayWriter(DisplayWriter &&other) noexcept : type_(other.type_) {
if (type_ == LAMBDA) {
this->f_ = other.f_;
other.f_ = nullptr;
} else if (type_ == STATELESS_LAMBDA) {
this->stateless_f_ = other.stateless_f_;
}
other.type_ = NONE;
}
// Assignment operators
DisplayWriter &operator=(const DisplayWriter &other) {
if (this != &other) {
this->~DisplayWriter();
new (this) DisplayWriter(other);
}
return *this;
}
DisplayWriter &operator=(DisplayWriter &&other) noexcept {
if (this != &other) {
this->~DisplayWriter();
new (this) DisplayWriter(std::move(other));
}
return *this;
}
~DisplayWriter() {
if (type_ == LAMBDA) {
delete this->f_;
}
// STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty)
}
bool has_value() const { return this->type_ != NONE; }
void call(T &display) const {
switch (this->type_) {
case STATELESS_LAMBDA:
this->stateless_f_(display); // Direct function pointer call
break;
case LAMBDA:
(*this->f_)(display); // std::function call
break;
case NONE:
default:
break;
}
}
// Operator() for convenience
void operator()(T &display) const { this->call(display); }
// Operator* for backwards compatibility with (*writer_)(*this) pattern
DisplayWriter &operator*() { return *this; }
const DisplayWriter &operator*() const { return *this; }
protected:
enum : uint8_t {
NONE,
LAMBDA,
STATELESS_LAMBDA,
} type_;
union {
std::function<void(T &)> *f_;
void (*stateless_f_)(T &);
};
};
// Type alias for Display writer - uses optimized DisplayWriter instead of std::function
using display_writer_t = DisplayWriter<Display>;
using display_writer_t = std::function<void(Display &)>;
#define LOG_DISPLAY(prefix, type, obj) \
if ((obj) != nullptr) { \
@@ -788,7 +678,7 @@ class Display : public PollingComponent {
void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3);
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
display_writer_t writer_{};
optional<display_writer_t> writer_{};
DisplayPage *page_{nullptr};
DisplayPage *previous_page_{nullptr};
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;

View File

@@ -231,7 +231,7 @@ void Fan::save_state_() {
state.direction = this->direction;
const char *preset = this->get_preset_mode();
if (preset != nullptr) {
if (traits.supports_preset_modes() && preset != nullptr) {
const auto &preset_modes = traits.supported_preset_modes();
// Find index of current preset mode (pointer comparison is safe since preset is from traits)
for (size_t i = 0; i < preset_modes.size(); i++) {

View File

@@ -1,25 +1,19 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODEL, CONF_VOLTAGE
from esphome.const import CONF_ID, CONF_VOLTAGE
CODEOWNERS = ["@jesserockz", "@sebydocky"]
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
gp8403_ns = cg.esphome_ns.namespace("gp8403")
GP8403Component = gp8403_ns.class_("GP8403Component", cg.Component, i2c.I2CDevice)
GP8403 = gp8403_ns.class_("GP8403", cg.Component, i2c.I2CDevice)
GP8403Voltage = gp8403_ns.enum("GP8403Voltage")
GP8403Model = gp8403_ns.enum("GP8403Model")
CONF_GP8403_ID = "gp8403_id"
MODELS = {
"GP8403": GP8403Model.GP8403,
"GP8413": GP8403Model.GP8413,
}
VOLTAGES = {
"5V": GP8403Voltage.GP8403_VOLTAGE_5V,
"10V": GP8403Voltage.GP8403_VOLTAGE_10V,
@@ -28,8 +22,7 @@ VOLTAGES = {
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GP8403Component),
cv.Optional(CONF_MODEL, default="GP8403"): cv.enum(MODELS, upper=True),
cv.GenerateID(): cv.declare_id(GP8403),
cv.Required(CONF_VOLTAGE): cv.enum(VOLTAGES, upper=True),
}
)
@@ -42,5 +35,5 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_model(config[CONF_MODEL]))
cg.add(var.set_voltage(config[CONF_VOLTAGE]))

View File

@@ -8,48 +8,16 @@ namespace gp8403 {
static const char *const TAG = "gp8403";
static const uint8_t RANGE_REGISTER = 0x01;
static const uint8_t OUTPUT_REGISTER = 0x02;
const LogString *model_to_string(GP8403Model model) {
switch (model) {
case GP8403Model::GP8403:
return LOG_STR("GP8403");
case GP8403Model::GP8413:
return LOG_STR("GP8413");
}
return LOG_STR("Unknown");
};
void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
void GP8403Component::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
void GP8403Component::dump_config() {
void GP8403::dump_config() {
ESP_LOGCONFIG(TAG,
"GP8403:\n"
" Voltage: %dV\n"
" Model: %s",
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10, LOG_STR_ARG(model_to_string(this->model_)));
" Voltage: %dV",
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10);
LOG_I2C_DEVICE(this);
}
void GP8403Component::write_state(float state, uint8_t channel) {
uint16_t val = 0;
switch (this->model_) {
case GP8403Model::GP8403:
val = ((uint16_t) (4095 * state)) << 4;
break;
case GP8403Model::GP8413:
val = ((uint16_t) (32767 * state)) << 1;
break;
default:
ESP_LOGE(TAG, "Unknown model %s", LOG_STR_ARG(model_to_string(this->model_)));
return;
}
ESP_LOGV(TAG, "Calculated DAC value: %" PRIu16, val);
i2c::ErrorCode err = this->write_register(OUTPUT_REGISTER + (2 * channel), (uint8_t *) &val, 2);
if (err != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Error writing to %s, code %d", LOG_STR_ARG(model_to_string(this->model_)), err);
}
}
} // namespace gp8403
} // namespace esphome

View File

@@ -11,24 +11,15 @@ enum GP8403Voltage {
GP8403_VOLTAGE_10V = 0x11,
};
enum GP8403Model {
GP8403,
GP8413,
};
class GP8403Component : public Component, public i2c::I2CDevice {
class GP8403 : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_model(GP8403Model model) { this->model_ = model; }
void set_voltage(gp8403::GP8403Voltage voltage) { this->voltage_ = voltage; }
void write_state(float state, uint8_t channel);
void set_voltage(gp8403::GP8403Voltage voltage) { this->voltage_ = voltage; }
protected:
GP8403Voltage voltage_;
GP8403Model model_{GP8403Model::GP8403};
};
} // namespace gp8403

View File

@@ -3,7 +3,7 @@ from esphome.components import i2c, output
import esphome.config_validation as cv
from esphome.const import CONF_CHANNEL, CONF_ID
from .. import CONF_GP8403_ID, GP8403Component, gp8403_ns
from .. import CONF_GP8403_ID, GP8403, gp8403_ns
DEPENDENCIES = ["gp8403"]
@@ -14,7 +14,7 @@ GP8403Output = gp8403_ns.class_(
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GP8403Output),
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403Component),
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1),
}
).extend(cv.COMPONENT_SCHEMA)

View File

@@ -7,6 +7,8 @@ namespace gp8403 {
static const char *const TAG = "gp8403.output";
static const uint8_t OUTPUT_REGISTER = 0x02;
void GP8403Output::dump_config() {
ESP_LOGCONFIG(TAG,
"GP8403 Output:\n"
@@ -14,7 +16,13 @@ void GP8403Output::dump_config() {
this->channel_);
}
void GP8403Output::write_state(float state) { this->parent_->write_state(state, this->channel_); }
void GP8403Output::write_state(float state) {
uint16_t value = ((uint16_t) (state * 4095)) << 4;
i2c::ErrorCode err = this->parent_->write_register(OUTPUT_REGISTER + (2 * this->channel_), (uint8_t *) &value, 2);
if (err != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Error writing to GP8403, code %d", err);
}
}
} // namespace gp8403
} // namespace esphome

View File

@@ -8,11 +8,13 @@
namespace esphome {
namespace gp8403 {
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403Component> {
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403> {
public:
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA - 1; }
void set_channel(uint8_t channel) { this->channel_ = channel; }
void write_state(float state) override;
protected:

View File

@@ -34,8 +34,8 @@ void GT911Touchscreen::setup() {
this->interrupt_pin_->digital_write(false);
}
delay(2);
this->reset_pin_->digital_write(true); // wait at least T3+T4 ms as per the datasheet
this->set_timeout(5 + 50 + 1, [this] { this->setup_internal_(); });
this->reset_pin_->digital_write(true); // wait 50ms after reset
this->set_timeout(50, [this] { this->setup_internal_(); });
return;
}
this->setup_internal_();

View File

@@ -2,18 +2,13 @@
#include "esphome/core/hal.h"
#include "esphome/components/lcd_base/lcd_display.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace lcd_gpio {
class GPIOLCDDisplay;
using gpio_lcd_writer_t = display::DisplayWriter<GPIOLCDDisplay>;
class GPIOLCDDisplay : public lcd_base::LCDDisplay {
public:
void set_writer(gpio_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
void set_writer(std::function<void(GPIOLCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void setup() override;
void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) {
this->data_pins_[0] = d0;
@@ -48,7 +43,7 @@ class GPIOLCDDisplay : public lcd_base::LCDDisplay {
GPIOPin *rw_pin_{nullptr};
GPIOPin *enable_pin_{nullptr};
GPIOPin *data_pins_[8]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
gpio_lcd_writer_t writer_;
std::function<void(GPIOLCDDisplay &)> writer_;
};
} // namespace lcd_gpio

View File

@@ -3,18 +3,13 @@
#include "esphome/core/component.h"
#include "esphome/components/lcd_base/lcd_display.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace lcd_pcf8574 {
class PCF8574LCDDisplay;
using pcf8574_lcd_writer_t = display::DisplayWriter<PCF8574LCDDisplay>;
class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
public:
void set_writer(pcf8574_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
void set_writer(std::function<void(PCF8574LCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void setup() override;
void dump_config() override;
void backlight();
@@ -29,7 +24,7 @@ class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
// Stores the current state of the backlight.
uint8_t backlight_value_;
pcf8574_lcd_writer_t writer_;
std::function<void(PCF8574LCDDisplay &)> writer_;
};
} // namespace lcd_pcf8574

View File

@@ -174,7 +174,7 @@ static uint8_t calc_checksum(void *data, size_t size) {
static int get_firmware_int(const char *version_string) {
std::string version_str = version_string;
if (version_str[0] == 'v') {
version_str.erase(0, 1);
version_str = version_str.substr(1);
}
version_str.erase(remove(version_str.begin(), version_str.end(), '.'), version_str.end());
int version_integer = stoi(version_str);

View File

@@ -1,5 +1,4 @@
import re
import textwrap
import esphome.config_validation as cv
from esphome.const import CONF_HEIGHT, CONF_TYPE, CONF_WIDTH
@@ -123,7 +122,7 @@ class FlexLayout(Layout):
def get_layout_schemas(self, config: dict) -> tuple:
layout = config.get(CONF_LAYOUT)
if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_FLEX:
if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_FLEX:
return None, {}
child_schema = FLEX_OBJ_SCHEMA
if grow := layout.get(CONF_FLEX_GROW):
@@ -162,8 +161,6 @@ class DirectionalLayout(FlexLayout):
return self.direction
def get_layout_schemas(self, config: dict) -> tuple:
if not isinstance(config.get(CONF_LAYOUT), str):
return None, {}
if config.get(CONF_LAYOUT, "").lower() != self.direction:
return None, {}
return cv.one_of(self.direction, lower=True), flex_hv_schema(self.direction)
@@ -209,7 +206,7 @@ class GridLayout(Layout):
# Not a valid grid layout string
return None, {}
if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_GRID:
if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_GRID:
return None, {}
return (
{
@@ -262,7 +259,7 @@ class GridLayout(Layout):
)
# should be guaranteed to be a dict at this point
assert isinstance(layout, dict)
assert layout.get(CONF_TYPE).lower() == TYPE_GRID
assert layout.get(CONF_TYPE) == TYPE_GRID
rows = len(layout[CONF_GRID_ROWS])
columns = len(layout[CONF_GRID_COLUMNS])
used_cells = [[None] * columns for _ in range(rows)]
@@ -338,17 +335,6 @@ def append_layout_schema(schema, config: dict):
if CONF_LAYOUT not in config:
# If no layout is specified, return the schema as is
return schema.extend({cv.Optional(CONF_WIDGETS): any_widget_schema()})
layout = config[CONF_LAYOUT]
# Sanity check the layout to avoid redundant checks in each type
if not isinstance(layout, str) and not isinstance(layout, dict):
raise cv.Invalid(
"The 'layout' option must be a string or a dictionary", [CONF_LAYOUT]
)
if isinstance(layout, dict) and not isinstance(layout.get(CONF_TYPE), str):
raise cv.Invalid(
"Invalid layout type; must be a string ('flex' or 'grid')",
[CONF_LAYOUT, CONF_TYPE],
)
for layout_class in LAYOUT_CLASSES:
layout_schema, child_schema = layout_class.get_layout_schemas(config)
@@ -362,17 +348,10 @@ def append_layout_schema(schema, config: dict):
layout_schema.add_extra(layout_class.validate)
return layout_schema.extend(schema)
if isinstance(layout, dict):
raise cv.Invalid(
"Invalid layout type; must be 'flex' or 'grid'", [CONF_LAYOUT, CONF_TYPE]
)
raise cv.Invalid(
textwrap.dedent(
"""
Invalid 'layout' value
layout choices are 'horizontal', 'vertical', '<rows>x<cols>',
or a dictionary with a 'type' key
"""
),
[CONF_LAYOUT],
# If no layout class matched, return a default schema
return cv.Schema(
{
cv.Optional(CONF_LAYOUT): cv.one_of(*LAYOUT_CHOICES, lower=True),
cv.Optional(CONF_WIDGETS): any_widget_schema(),
}
)

View File

@@ -59,8 +59,8 @@ class LVGLSelect : public select::Select, public Component {
const auto &opts = this->widget_->get_options();
FixedVector<const char *> opt_ptrs;
opt_ptrs.init(opts.size());
for (const auto &opt : opts) {
opt_ptrs.push_back(opt.c_str());
for (size_t i = 0; i < opts.size(); i++) {
opt_ptrs[i] = opts[i].c_str();
}
this->traits.set_options(opt_ptrs);
}

View File

@@ -4,14 +4,13 @@
#include "esphome/core/time.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace max7219 {
class MAX7219Component;
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
using max7219_writer_t = std::function<void(MAX7219Component &)>;
class MAX7219Component : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
@@ -58,7 +57,7 @@ class MAX7219Component : public PollingComponent,
uint8_t num_chips_{1};
uint8_t *buffer_;
bool reverse_{false};
max7219_writer_t writer_{};
optional<max7219_writer_t> writer_{};
};
} // namespace max7219

View File

@@ -271,11 +271,7 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
}
}
} else if (this->orientation_ == 1) {
if (this->flip_x_) {
b = pixels[7 - col];
} else {
b = pixels[col];
}
b = pixels[col];
} else if (this->orientation_ == 2) {
for (uint8_t i = 0; i < 8; i++) {
if (this->flip_x_) {
@@ -286,11 +282,7 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
}
} else {
for (uint8_t i = 0; i < 8; i++) {
if (this->flip_x_) {
b |= ((pixels[col] >> i) & 1) << (7 - i);
} else {
b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
}
b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
}
}
// send this byte to display at selected chip

View File

@@ -23,7 +23,7 @@ enum ScrollMode {
class MAX7219Component;
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
using max7219_writer_t = std::function<void(MAX7219Component &)>;
class MAX7219Component : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
@@ -117,7 +117,7 @@ class MAX7219Component : public display::DisplayBuffer,
uint32_t last_scroll_ = 0;
uint16_t stepsleft_;
size_t get_buffer_length_();
max7219_writer_t writer_local_{};
optional<max7219_writer_t> writer_local_{};
};
} // namespace max7219digit

View File

@@ -3,7 +3,6 @@ import binascii
from esphome import automation
import esphome.codegen as cg
from esphome.components import modbus
from esphome.components.const import CONF_ENABLED
import esphome.config_validation as cv
from esphome.const import (
CONF_ADDRESS,
@@ -21,6 +20,7 @@ from .const import (
CONF_BYTE_OFFSET,
CONF_COMMAND_THROTTLE,
CONF_CUSTOM_COMMAND,
CONF_ENABLED,
CONF_FORCE_NEW_RANGE,
CONF_MAX_CMD_RETRIES,
CONF_MODBUS_CONTROLLER_ID,

View File

@@ -2,6 +2,7 @@ CONF_ALLOW_DUPLICATE_COMMANDS = "allow_duplicate_commands"
CONF_BITMASK = "bitmask"
CONF_BYTE_OFFSET = "byte_offset"
CONF_COMMAND_THROTTLE = "command_throttle"
CONF_ENABLED = "enabled"
CONF_OFFLINE_SKIP_UPDATES = "offline_skip_updates"
CONF_CUSTOM_COMMAND = "custom_command"
CONF_FORCE_NEW_RANGE = "force_new_range"

View File

@@ -9,7 +9,6 @@
#include "esphome/components/uart/uart.h"
#include "nextion_base.h"
#include "nextion_component.h"
#include "esphome/components/display/display.h"
#include "esphome/components/display/display_color_utils.h"
#ifdef USE_NEXTION_TFT_UPLOAD
@@ -32,7 +31,7 @@ namespace nextion {
class Nextion;
class NextionComponentBase;
using nextion_writer_t = display::DisplayWriter<Nextion>;
using nextion_writer_t = std::function<void(Nextion &)>;
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
@@ -1472,7 +1471,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
CallbackManager<void(uint8_t, uint8_t, bool)> touch_callback_{};
CallbackManager<void()> buffer_overflow_callback_{};
nextion_writer_t writer_;
optional<nextion_writer_t> writer_;
optional<float> brightness_;
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO

View File

@@ -3,7 +3,6 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/display/display.h"
#include <cinttypes>
@@ -30,7 +29,7 @@ enum UNIT {
UNIT_DEG_E, ///< show "°E"
};
using pvvx_writer_t = display::DisplayWriter<PVVXDisplay>;
using pvvx_writer_t = std::function<void(PVVXDisplay &)>;
class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
public:
@@ -127,7 +126,7 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
esp32_ble_tracker::ESPBTUUID char_uuid_ =
esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb");
pvvx_writer_t writer_{};
optional<pvvx_writer_t> writer_{};
};
} // namespace pvvx_mithermometer

View File

@@ -71,7 +71,6 @@ static const uint16_t FALLBACK_FREQUENCY = 64767U; // To use with frequency = 0
static const uint32_t MICROSECONDS_IN_SECONDS = 1000000UL;
static const uint16_t PRONTO_DEFAULT_GAP = 45000;
static const uint16_t MARK_EXCESS_MICROS = 20;
static constexpr size_t PRONTO_LOG_CHUNK_SIZE = 230;
static uint16_t to_frequency_k_hz(uint16_t code) {
if (code == 0)
@@ -226,17 +225,18 @@ optional<ProntoData> ProntoProtocol::decode(RemoteReceiveData src) {
}
void ProntoProtocol::dump(const ProntoData &data) {
std::string rest = data.data;
std::string rest;
rest = data.data;
ESP_LOGI(TAG, "Received Pronto: data=");
do {
size_t chunk_size = rest.size() > PRONTO_LOG_CHUNK_SIZE ? PRONTO_LOG_CHUNK_SIZE : rest.size();
ESP_LOGI(TAG, "%.*s", (int) chunk_size, rest.c_str());
if (rest.size() > PRONTO_LOG_CHUNK_SIZE) {
rest.erase(0, PRONTO_LOG_CHUNK_SIZE);
while (true) {
ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str());
if (rest.size() > 230) {
rest = rest.substr(230);
} else {
break;
}
} while (true);
}
}
} // namespace remote_base

View File

@@ -35,7 +35,9 @@ void Rtttl::dump_config() {
void Rtttl::play(std::string rtttl) {
if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) {
ESP_LOGW(TAG, "Already playing: %.*s", (int) this->rtttl_.find(':'), this->rtttl_.c_str());
int pos = this->rtttl_.find(':');
auto name = this->rtttl_.substr(0, pos);
ESP_LOGW(TAG, "Already playing: %s", name.c_str());
return;
}
@@ -57,7 +59,8 @@ void Rtttl::play(std::string rtttl) {
return;
}
ESP_LOGD(TAG, "Playing song %.*s", (int) this->position_, this->rtttl_.c_str());
auto name = this->rtttl_.substr(0, this->position_);
ESP_LOGD(TAG, "Playing song %s", name.c_str());
// get default duration
this->position_ = this->rtttl_.find("d=", this->position_);

View File

@@ -35,7 +35,7 @@ void Select::publish_state(size_t index) {
this->state_callback_.call(std::string(option), index);
}
const char *Select::current_option() const { return this->has_state() ? this->option_at(this->active_index_) : ""; }
const char *Select::current_option() const { return this->option_at(this->active_index_); }
void Select::add_on_state_callback(std::function<void(std::string, size_t)> &&callback) {
this->state_callback_.add(std::move(callback));

View File

@@ -93,14 +93,19 @@ class Select : public EntityBase {
size_t active_index_{0};
/** Set the value of the select, this is a virtual method that each select integration can implement.
*
* IMPORTANT: At least ONE of the two control() methods must be overridden by derived classes.
* Overriding control(size_t) is PREFERRED as it avoids string conversions.
*
* This method is called by control(size_t) when not overridden, or directly by external code.
* Integrations can either:
* 1. Override this method to handle string-based control (traditional approach)
* 2. Override control(size_t) instead to work with indices directly (recommended)
*
* Default implementation converts to index and calls control(size_t).
*
* @param value The value as validated by the caller.
* Delegation chain:
* - SelectCall::perform() → control(size_t) → [if not overridden] → control(string)
* - External code → control(string) → publish_state(string) → publish_state(size_t)
*
* @param value The value as validated by the SelectCall.
*/
virtual void control(const std::string &value) {
auto index = this->index_of(value);

View File

@@ -7,8 +7,8 @@ void SelectTraits::set_options(const std::initializer_list<const char *> &option
void SelectTraits::set_options(const FixedVector<const char *> &options) {
this->options_.init(options.size());
for (const auto &opt : options) {
this->options_.push_back(opt);
for (size_t i = 0; i < options.size(); i++) {
this->options_[i] = options[i];
}
}

View File

@@ -78,7 +78,7 @@ void SGP30Component::setup() {
uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(this->serial_number_));
this->pref_ = global_preferences->make_preference<SGP30Baselines>(hash, true);
if (this->store_baseline_ && this->pref_.load(&this->baselines_storage_)) {
if (this->pref_.load(&this->baselines_storage_)) {
ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2,
baselines_storage_.tvoc);
this->eco2_baseline_ = this->baselines_storage_.eco2;

View File

@@ -9,7 +9,7 @@ namespace st7920 {
class ST7920;
using st7920_writer_t = display::DisplayWriter<ST7920>;
using st7920_writer_t = std::function<void(ST7920 &)>;
class ST7920 : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
@@ -44,7 +44,7 @@ class ST7920 : public display::DisplayBuffer,
void end_transaction_();
int16_t width_ = 128, height_ = 64;
st7920_writer_t writer_local_{};
optional<st7920_writer_t> writer_local_{};
};
} // namespace st7920

View File

@@ -3,14 +3,13 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace tm1621 {
class TM1621Display;
using tm1621_writer_t = display::DisplayWriter<TM1621Display>;
using tm1621_writer_t = std::function<void(TM1621Display &)>;
class TM1621Display : public PollingComponent {
public:
@@ -60,7 +59,7 @@ class TM1621Display : public PollingComponent {
GPIOPin *cs_pin_;
GPIOPin *read_pin_;
GPIOPin *write_pin_;
tm1621_writer_t writer_{};
optional<tm1621_writer_t> writer_{};
char row_[2][12];
uint8_t state_;
uint8_t device_;

View File

@@ -4,7 +4,6 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/time.h"
#include "esphome/components/display/display.h"
#include <vector>
@@ -20,7 +19,7 @@ class TM1637Display;
class TM1637Key;
#endif
using tm1637_writer_t = display::DisplayWriter<TM1637Display>;
using tm1637_writer_t = std::function<void(TM1637Display &)>;
class TM1637Display : public PollingComponent {
public:
@@ -79,7 +78,7 @@ class TM1637Display : public PollingComponent {
uint8_t length_;
bool inverted_;
bool on_{true};
tm1637_writer_t writer_{};
optional<tm1637_writer_t> writer_{};
uint8_t buffer_[6] = {0};
#ifdef USE_BINARY_SENSOR
std::vector<TM1637Key *> tm1637_keys_{};

View File

@@ -5,7 +5,6 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/time.h"
#include "esphome/components/display/display.h"
#include <vector>
@@ -19,7 +18,7 @@ class KeyListener {
class TM1638Component;
using tm1638_writer_t = display::DisplayWriter<TM1638Component>;
using tm1638_writer_t = std::function<void(TM1638Component &)>;
class TM1638Component : public PollingComponent {
public:
@@ -71,7 +70,7 @@ class TM1638Component : public PollingComponent {
GPIOPin *stb_pin_;
GPIOPin *dio_pin_;
uint8_t *buffer_ = new uint8_t[8];
tm1638_writer_t writer_{};
optional<tm1638_writer_t> writer_{};
std::vector<KeyListener *> listeners_{};
};

View File

@@ -657,8 +657,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
ESP_LOGW(TAG, "No text in STT_END event");
return;
} else if (text.length() > 500) {
text.resize(497);
text += "...";
text = text.substr(0, 497) + "...";
}
ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
this->defer([this, text]() { this->stt_end_trigger_->trigger(text); });
@@ -715,8 +714,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
return;
}
if (text.length() > 500) {
text.resize(497);
text += "...";
text = text.substr(0, 497) + "...";
}
ESP_LOGD(TAG, "Response: \"%s\"", text.c_str());
this->defer([this, text]() {

View File

@@ -10,8 +10,6 @@
namespace esphome {
namespace wifi_info {
static constexpr size_t MAX_STATE_LENGTH = 255;
class IPAddressWiFiInfo : public PollingComponent, public text_sensor::TextSensor {
public:
void update() override {
@@ -73,14 +71,11 @@ class ScanResultsWiFiInfo : public PollingComponent, public text_sensor::TextSen
scan_results += "dB\n";
}
// There's a limit of 255 characters per state.
// Longer states just don't get sent so we truncate it.
if (scan_results.length() > MAX_STATE_LENGTH) {
scan_results.resize(MAX_STATE_LENGTH);
}
if (this->last_scan_results_ != scan_results) {
this->last_scan_results_ = scan_results;
this->publish_state(scan_results);
// There's a limit of 255 characters per state.
// Longer states just don't get sent so we truncate it.
this->publish_state(scan_results.substr(0, 255));
}
}
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }

View File

@@ -1,6 +1,5 @@
import esphome.codegen as cg
from esphome.components import binary_sensor
from esphome.components.const import CONF_ENABLED
import esphome.config_validation as cv
from esphome.const import (
CONF_STATUS,
@@ -10,6 +9,8 @@ from esphome.const import (
from . import CONF_WIREGUARD_ID, Wireguard
CONF_ENABLED = "enabled"
DEPENDENCIES = ["wireguard"]
CONFIG_SCHEMA = {

View File

@@ -251,8 +251,6 @@ template<typename T> class FixedVector {
}
// Allocate capacity - can be called multiple times to reinit
// IMPORTANT: After calling init(), you MUST use push_back() to add elements.
// Direct assignment via operator[] does NOT update the size counter.
void init(size_t n) {
cleanup_();
reset_();

View File

@@ -4,11 +4,6 @@ gp8403:
voltage: 5V
- id: gp8403_10v
i2c_id: i2c_bus
model: GP8403
voltage: 10V
- id: gp8413
i2c_id: i2c_bus
model: GP8413
voltage: 10V
output:
@@ -20,7 +15,3 @@ output:
gp8403_id: gp8403_10v
id: gp8403_output_1
channel: 1
- platform: gp8403
gp8403_id: gp8413
id: gp8413_output_2
channel: 1

View File

@@ -1,6 +1,5 @@
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480

View File

@@ -191,7 +191,7 @@ lvgl:
args: ['lv_event_code_name_for(event->code).c_str()']
skip: true
layout:
type: Flex
type: flex
pad_row: 4
pad_column: 4px
flex_align_main: center
@@ -863,7 +863,7 @@ lvgl:
width: 100%
pad_all: 8
layout:
type: GRid
type: grid
grid_row_align: end
grid_rows: [25px, fr(1), content]
grid_columns: [40, fr(1), fr(1)]
@@ -1014,7 +1014,7 @@ lvgl:
r_mod: -20
opa: 0%
- id: page3
layout: Horizontal
layout: horizontal
widgets:
- keyboard:
id: lv_keyboard

View File

@@ -13,14 +13,11 @@ display:
binary_sensor:
- platform: sdl
sdl_id: sdl_display
id: key_up
key: SDLK_UP
- platform: sdl
sdl_id: sdl_display
id: key_down
key: SDLK_DOWN
- platform: sdl
sdl_id: sdl_display
id: key_enter
key: SDLK_RETURN