mirror of
https://github.com/esphome/esphome.git
synced 2025-11-05 09:31:54 +00:00
Compare commits
25 Commits
memory_api
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c70d154276 | ||
|
|
358296a57e | ||
|
|
e0831abcd3 | ||
|
|
34208138c1 | ||
|
|
5855f3ce33 | ||
|
|
f420a8f32d | ||
|
|
a0755829bf | ||
|
|
009d6a15f6 | ||
|
|
209091e6a4 | ||
|
|
bf83b70a18 | ||
|
|
d70fe126f6 | ||
|
|
0e3f2d3302 | ||
|
|
32975c9d8b | ||
|
|
1446e7174a | ||
|
|
64f8963566 | ||
|
|
6f7e54c3f3 | ||
|
|
c7ae424613 | ||
|
|
c5e5609e92 | ||
|
|
885508775f | ||
|
|
531b27582a | ||
|
|
aed7505f53 | ||
|
|
191a88c2dc | ||
|
|
968df6cb3f | ||
|
|
71fa88c9d4 | ||
|
|
84f7cacef9 |
@@ -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
|
||||
esphome/components/gp8403/* @jesserockz @sebydocky
|
||||
esphome/components/gpio/* @esphome/core
|
||||
esphome/components/gpio/one_wire/* @ssieb
|
||||
esphome/components/gps/* @coogle @ximex
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""CLI interface for memory analysis with report generation."""
|
||||
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import sys
|
||||
|
||||
from . import (
|
||||
@@ -284,28 +283,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Export analysis results as JSON."""
|
||||
data = {
|
||||
"components": {
|
||||
name: {
|
||||
"text": mem.text_size,
|
||||
"rodata": mem.rodata_size,
|
||||
"data": mem.data_size,
|
||||
"bss": mem.bss_size,
|
||||
"flash_total": mem.flash_total,
|
||||
"ram_total": mem.ram_total,
|
||||
"symbol_count": mem.symbol_count,
|
||||
}
|
||||
for name, mem in self.components.items()
|
||||
},
|
||||
"totals": {
|
||||
"flash": sum(c.flash_total for c in self.components.values()),
|
||||
"ram": sum(c.ram_total for c in self.components.values()),
|
||||
},
|
||||
}
|
||||
return json.dumps(data, indent=2)
|
||||
|
||||
def dump_uncategorized_symbols(self, output_file: str | None = None) -> None:
|
||||
"""Dump uncategorized symbols for analysis."""
|
||||
# Sort by size descending
|
||||
|
||||
@@ -8,6 +8,7 @@ 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"
|
||||
|
||||
@@ -176,7 +176,117 @@ class Display;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
|
||||
using display_writer_t = std::function<void(Display &)>;
|
||||
/** 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>;
|
||||
|
||||
#define LOG_DISPLAY(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
@@ -678,7 +788,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};
|
||||
optional<display_writer_t> writer_{};
|
||||
display_writer_t writer_{};
|
||||
DisplayPage *page_{nullptr};
|
||||
DisplayPage *previous_page_{nullptr};
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
|
||||
@@ -389,7 +389,8 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
if (this->conn_id_ != param->search_res.conn_id)
|
||||
return false;
|
||||
this->service_count_++;
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE ||
|
||||
this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
|
||||
// V3 clients don't need services initialized since
|
||||
// as they use the ESP APIs to get services.
|
||||
break;
|
||||
|
||||
@@ -15,10 +15,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
@@ -30,10 +27,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
|
||||
@@ -231,7 +231,7 @@ void Fan::save_state_() {
|
||||
state.direction = this->direction;
|
||||
|
||||
const char *preset = this->get_preset_mode();
|
||||
if (traits.supports_preset_modes() && preset != nullptr) {
|
||||
if (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++) {
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_VOLTAGE
|
||||
from esphome.const import CONF_ID, CONF_MODEL, CONF_VOLTAGE
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
CODEOWNERS = ["@jesserockz", "@sebydocky"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
gp8403_ns = cg.esphome_ns.namespace("gp8403")
|
||||
GP8403 = gp8403_ns.class_("GP8403", cg.Component, i2c.I2CDevice)
|
||||
GP8403Component = gp8403_ns.class_("GP8403Component", 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,
|
||||
@@ -22,7 +28,8 @@ VOLTAGES = {
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GP8403),
|
||||
cv.GenerateID(): cv.declare_id(GP8403Component),
|
||||
cv.Optional(CONF_MODEL, default="GP8403"): cv.enum(MODELS, upper=True),
|
||||
cv.Required(CONF_VOLTAGE): cv.enum(VOLTAGES, upper=True),
|
||||
}
|
||||
)
|
||||
@@ -35,5 +42,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]))
|
||||
|
||||
@@ -8,16 +8,48 @@ namespace gp8403 {
|
||||
static const char *const TAG = "gp8403";
|
||||
|
||||
static const uint8_t RANGE_REGISTER = 0x01;
|
||||
static const uint8_t OUTPUT_REGISTER = 0x02;
|
||||
|
||||
void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
|
||||
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::dump_config() {
|
||||
void GP8403Component::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
|
||||
|
||||
void GP8403Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"GP8403:\n"
|
||||
" Voltage: %dV",
|
||||
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10);
|
||||
" Voltage: %dV\n"
|
||||
" Model: %s",
|
||||
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10, LOG_STR_ARG(model_to_string(this->model_)));
|
||||
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
|
||||
|
||||
@@ -11,15 +11,24 @@ enum GP8403Voltage {
|
||||
GP8403_VOLTAGE_10V = 0x11,
|
||||
};
|
||||
|
||||
class GP8403 : public Component, public i2c::I2CDevice {
|
||||
enum GP8403Model {
|
||||
GP8403,
|
||||
GP8413,
|
||||
};
|
||||
|
||||
class GP8403Component : 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);
|
||||
|
||||
protected:
|
||||
GP8403Voltage voltage_;
|
||||
GP8403Model model_{GP8403Model::GP8403};
|
||||
};
|
||||
|
||||
} // namespace gp8403
|
||||
|
||||
@@ -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, GP8403, gp8403_ns
|
||||
from .. import CONF_GP8403_ID, GP8403Component, 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(GP8403),
|
||||
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403Component),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -7,8 +7,6 @@ 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"
|
||||
@@ -16,13 +14,7 @@ void GP8403Output::dump_config() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
void GP8403Output::write_state(float state) { this->parent_->write_state(state, this->channel_); }
|
||||
|
||||
} // namespace gp8403
|
||||
} // namespace esphome
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
namespace esphome {
|
||||
namespace gp8403 {
|
||||
|
||||
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403> {
|
||||
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403Component> {
|
||||
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:
|
||||
|
||||
@@ -34,8 +34,8 @@ void GT911Touchscreen::setup() {
|
||||
this->interrupt_pin_->digital_write(false);
|
||||
}
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true); // wait 50ms after reset
|
||||
this->set_timeout(50, [this] { this->setup_internal_(); });
|
||||
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_(); });
|
||||
return;
|
||||
}
|
||||
this->setup_internal_();
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
|
||||
#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(std::function<void(GPIOLCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
|
||||
void set_writer(gpio_lcd_writer_t &&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;
|
||||
@@ -43,7 +48,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};
|
||||
std::function<void(GPIOLCDDisplay &)> writer_;
|
||||
gpio_lcd_writer_t writer_;
|
||||
};
|
||||
|
||||
} // namespace lcd_gpio
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
#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(std::function<void(PCF8574LCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
|
||||
void set_writer(pcf8574_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void backlight();
|
||||
@@ -24,7 +29,7 @@ class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
|
||||
|
||||
// Stores the current state of the backlight.
|
||||
uint8_t backlight_value_;
|
||||
std::function<void(PCF8574LCDDisplay &)> writer_;
|
||||
pcf8574_lcd_writer_t writer_;
|
||||
};
|
||||
|
||||
} // namespace lcd_pcf8574
|
||||
|
||||
@@ -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 = version_str.substr(1);
|
||||
version_str.erase(0, 1);
|
||||
}
|
||||
version_str.erase(remove(version_str.begin(), version_str.end(), '.'), version_str.end());
|
||||
int version_integer = stoi(version_str);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_HEIGHT, CONF_TYPE, CONF_WIDTH
|
||||
@@ -122,7 +123,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) != TYPE_FLEX:
|
||||
if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_FLEX:
|
||||
return None, {}
|
||||
child_schema = FLEX_OBJ_SCHEMA
|
||||
if grow := layout.get(CONF_FLEX_GROW):
|
||||
@@ -161,6 +162,8 @@ 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)
|
||||
@@ -206,7 +209,7 @@ class GridLayout(Layout):
|
||||
# Not a valid grid layout string
|
||||
return None, {}
|
||||
|
||||
if not isinstance(layout, dict) or layout.get(CONF_TYPE) != TYPE_GRID:
|
||||
if not isinstance(layout, dict) or layout.get(CONF_TYPE).lower() != TYPE_GRID:
|
||||
return None, {}
|
||||
return (
|
||||
{
|
||||
@@ -259,7 +262,7 @@ class GridLayout(Layout):
|
||||
)
|
||||
# should be guaranteed to be a dict at this point
|
||||
assert isinstance(layout, dict)
|
||||
assert layout.get(CONF_TYPE) == TYPE_GRID
|
||||
assert layout.get(CONF_TYPE).lower() == TYPE_GRID
|
||||
rows = len(layout[CONF_GRID_ROWS])
|
||||
columns = len(layout[CONF_GRID_COLUMNS])
|
||||
used_cells = [[None] * columns for _ in range(rows)]
|
||||
@@ -335,6 +338,17 @@ 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)
|
||||
@@ -348,10 +362,17 @@ def append_layout_schema(schema, config: dict):
|
||||
layout_schema.add_extra(layout_class.validate)
|
||||
return layout_schema.extend(schema)
|
||||
|
||||
# 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(),
|
||||
}
|
||||
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],
|
||||
)
|
||||
|
||||
@@ -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 (size_t i = 0; i < opts.size(); i++) {
|
||||
opt_ptrs[i] = opts[i].c_str();
|
||||
for (const auto &opt : opts) {
|
||||
opt_ptrs.push_back(opt.c_str());
|
||||
}
|
||||
this->traits.set_options(opt_ptrs);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
#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 = std::function<void(MAX7219Component &)>;
|
||||
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
|
||||
|
||||
class MAX7219Component : public PollingComponent,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
@@ -57,7 +58,7 @@ class MAX7219Component : public PollingComponent,
|
||||
uint8_t num_chips_{1};
|
||||
uint8_t *buffer_;
|
||||
bool reverse_{false};
|
||||
optional<max7219_writer_t> writer_{};
|
||||
max7219_writer_t writer_{};
|
||||
};
|
||||
|
||||
} // namespace max7219
|
||||
|
||||
@@ -271,7 +271,11 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||
}
|
||||
}
|
||||
} else if (this->orientation_ == 1) {
|
||||
b = pixels[col];
|
||||
if (this->flip_x_) {
|
||||
b = pixels[7 - col];
|
||||
} else {
|
||||
b = pixels[col];
|
||||
}
|
||||
} else if (this->orientation_ == 2) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (this->flip_x_) {
|
||||
@@ -282,7 +286,11 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
|
||||
}
|
||||
} else {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
|
||||
if (this->flip_x_) {
|
||||
b |= ((pixels[col] >> i) & 1) << (7 - i);
|
||||
} else {
|
||||
b |= ((pixels[7 - col] >> i) & 1) << (7 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
// send this byte to display at selected chip
|
||||
|
||||
@@ -23,7 +23,7 @@ enum ScrollMode {
|
||||
|
||||
class MAX7219Component;
|
||||
|
||||
using max7219_writer_t = std::function<void(MAX7219Component &)>;
|
||||
using max7219_writer_t = display::DisplayWriter<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_();
|
||||
optional<max7219_writer_t> writer_local_{};
|
||||
max7219_writer_t writer_local_{};
|
||||
};
|
||||
|
||||
} // namespace max7219digit
|
||||
|
||||
@@ -3,6 +3,7 @@ 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,
|
||||
@@ -20,7 +21,6 @@ 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,
|
||||
|
||||
@@ -2,7 +2,6 @@ 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"
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#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
|
||||
@@ -31,7 +32,7 @@ namespace nextion {
|
||||
class Nextion;
|
||||
class NextionComponentBase;
|
||||
|
||||
using nextion_writer_t = std::function<void(Nextion &)>;
|
||||
using nextion_writer_t = display::DisplayWriter<Nextion>;
|
||||
|
||||
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
|
||||
|
||||
@@ -1471,7 +1472,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
|
||||
CallbackManager<void(uint8_t, uint8_t, bool)> touch_callback_{};
|
||||
CallbackManager<void()> buffer_overflow_callback_{};
|
||||
|
||||
optional<nextion_writer_t> writer_;
|
||||
nextion_writer_t writer_;
|
||||
optional<float> brightness_;
|
||||
|
||||
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO
|
||||
|
||||
@@ -161,6 +161,9 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_OPENTHREAD")
|
||||
|
||||
# OpenThread uses esp_vfs_eventfd which requires VFS select support
|
||||
require_vfs_select()
|
||||
|
||||
# OpenThread SRP needs access to mDNS services after setup
|
||||
enable_mdns_storage()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#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>
|
||||
|
||||
@@ -29,7 +30,7 @@ enum UNIT {
|
||||
UNIT_DEG_E, ///< show "°E"
|
||||
};
|
||||
|
||||
using pvvx_writer_t = std::function<void(PVVXDisplay &)>;
|
||||
using pvvx_writer_t = display::DisplayWriter<PVVXDisplay>;
|
||||
|
||||
class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
|
||||
public:
|
||||
@@ -126,7 +127,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");
|
||||
|
||||
optional<pvvx_writer_t> writer_{};
|
||||
pvvx_writer_t writer_{};
|
||||
};
|
||||
|
||||
} // namespace pvvx_mithermometer
|
||||
|
||||
@@ -71,6 +71,7 @@ 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)
|
||||
@@ -225,18 +226,17 @@ optional<ProntoData> ProntoProtocol::decode(RemoteReceiveData src) {
|
||||
}
|
||||
|
||||
void ProntoProtocol::dump(const ProntoData &data) {
|
||||
std::string rest;
|
||||
|
||||
rest = data.data;
|
||||
std::string rest = data.data;
|
||||
ESP_LOGI(TAG, "Received Pronto: data=");
|
||||
while (true) {
|
||||
ESP_LOGI(TAG, "%s", rest.substr(0, 230).c_str());
|
||||
if (rest.size() > 230) {
|
||||
rest = rest.substr(230);
|
||||
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);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
} // namespace remote_base
|
||||
|
||||
@@ -35,9 +35,7 @@ void Rtttl::dump_config() {
|
||||
|
||||
void Rtttl::play(std::string rtttl) {
|
||||
if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) {
|
||||
int pos = this->rtttl_.find(':');
|
||||
auto name = this->rtttl_.substr(0, pos);
|
||||
ESP_LOGW(TAG, "Already playing: %s", name.c_str());
|
||||
ESP_LOGW(TAG, "Already playing: %.*s", (int) this->rtttl_.find(':'), this->rtttl_.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,8 +57,7 @@ void Rtttl::play(std::string rtttl) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto name = this->rtttl_.substr(0, this->position_);
|
||||
ESP_LOGD(TAG, "Playing song %s", name.c_str());
|
||||
ESP_LOGD(TAG, "Playing song %.*s", (int) this->position_, this->rtttl_.c_str());
|
||||
|
||||
// get default duration
|
||||
this->position_ = this->rtttl_.find("d=", this->position_);
|
||||
|
||||
@@ -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->option_at(this->active_index_); }
|
||||
const char *Select::current_option() const { return this->has_state() ? 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));
|
||||
|
||||
@@ -94,18 +94,13 @@ class Select : public EntityBase {
|
||||
|
||||
/** Set the value of the select, this is a virtual method that each select integration can implement.
|
||||
*
|
||||
* 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)
|
||||
* 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.
|
||||
* Default implementation converts to index and calls control(size_t).
|
||||
*
|
||||
* 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.
|
||||
* @param value The value as validated by the caller.
|
||||
*/
|
||||
virtual void control(const std::string &value) {
|
||||
auto index = this->index_of(value);
|
||||
|
||||
@@ -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 (size_t i = 0; i < options.size(); i++) {
|
||||
this->options_[i] = options[i];
|
||||
for (const auto &opt : options) {
|
||||
this->options_.push_back(opt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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->pref_.load(&this->baselines_storage_)) {
|
||||
if (this->store_baseline_ && 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;
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace st7920 {
|
||||
|
||||
class ST7920;
|
||||
|
||||
using st7920_writer_t = std::function<void(ST7920 &)>;
|
||||
using st7920_writer_t = display::DisplayWriter<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;
|
||||
optional<st7920_writer_t> writer_local_{};
|
||||
st7920_writer_t writer_local_{};
|
||||
};
|
||||
|
||||
} // namespace st7920
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
#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 = std::function<void(TM1621Display &)>;
|
||||
using tm1621_writer_t = display::DisplayWriter<TM1621Display>;
|
||||
|
||||
class TM1621Display : public PollingComponent {
|
||||
public:
|
||||
@@ -59,7 +60,7 @@ class TM1621Display : public PollingComponent {
|
||||
GPIOPin *cs_pin_;
|
||||
GPIOPin *read_pin_;
|
||||
GPIOPin *write_pin_;
|
||||
optional<tm1621_writer_t> writer_{};
|
||||
tm1621_writer_t writer_{};
|
||||
char row_[2][12];
|
||||
uint8_t state_;
|
||||
uint8_t device_;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#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 +20,7 @@ class TM1637Display;
|
||||
class TM1637Key;
|
||||
#endif
|
||||
|
||||
using tm1637_writer_t = std::function<void(TM1637Display &)>;
|
||||
using tm1637_writer_t = display::DisplayWriter<TM1637Display>;
|
||||
|
||||
class TM1637Display : public PollingComponent {
|
||||
public:
|
||||
@@ -78,7 +79,7 @@ class TM1637Display : public PollingComponent {
|
||||
uint8_t length_;
|
||||
bool inverted_;
|
||||
bool on_{true};
|
||||
optional<tm1637_writer_t> writer_{};
|
||||
tm1637_writer_t writer_{};
|
||||
uint8_t buffer_[6] = {0};
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
std::vector<TM1637Key *> tm1637_keys_{};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/time.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -18,7 +19,7 @@ class KeyListener {
|
||||
|
||||
class TM1638Component;
|
||||
|
||||
using tm1638_writer_t = std::function<void(TM1638Component &)>;
|
||||
using tm1638_writer_t = display::DisplayWriter<TM1638Component>;
|
||||
|
||||
class TM1638Component : public PollingComponent {
|
||||
public:
|
||||
@@ -70,7 +71,7 @@ class TM1638Component : public PollingComponent {
|
||||
GPIOPin *stb_pin_;
|
||||
GPIOPin *dio_pin_;
|
||||
uint8_t *buffer_ = new uint8_t[8];
|
||||
optional<tm1638_writer_t> writer_{};
|
||||
tm1638_writer_t writer_{};
|
||||
std::vector<KeyListener *> listeners_{};
|
||||
};
|
||||
|
||||
|
||||
@@ -657,7 +657,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
||||
ESP_LOGW(TAG, "No text in STT_END event");
|
||||
return;
|
||||
} else if (text.length() > 500) {
|
||||
text = text.substr(0, 497) + "...";
|
||||
text.resize(497);
|
||||
text += "...";
|
||||
}
|
||||
ESP_LOGD(TAG, "Speech recognised as: \"%s\"", text.c_str());
|
||||
this->defer([this, text]() { this->stt_end_trigger_->trigger(text); });
|
||||
@@ -714,7 +715,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
||||
return;
|
||||
}
|
||||
if (text.length() > 500) {
|
||||
text = text.substr(0, 497) + "...";
|
||||
text.resize(497);
|
||||
text += "...";
|
||||
}
|
||||
ESP_LOGD(TAG, "Response: \"%s\"", text.c_str());
|
||||
this->defer([this, text]() {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
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 {
|
||||
@@ -71,11 +73,14 @@ 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;
|
||||
// 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));
|
||||
this->publish_state(scan_results);
|
||||
}
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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,
|
||||
@@ -9,8 +10,6 @@ from esphome.const import (
|
||||
|
||||
from . import CONF_WIREGUARD_ID, Wireguard
|
||||
|
||||
CONF_ENABLED = "enabled"
|
||||
|
||||
DEPENDENCIES = ["wireguard"]
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
|
||||
@@ -251,6 +251,8 @@ 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_();
|
||||
|
||||
@@ -94,9 +94,10 @@ class Scheduler {
|
||||
} name_;
|
||||
uint32_t interval;
|
||||
// Split time to handle millis() rollover. The scheduler combines the 32-bit millis()
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (stored as 64-bit
|
||||
// for compatibility). With 49.7 days per 32-bit rollover, the 16-bit counter
|
||||
// supports 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (using 32+16 bits).
|
||||
// This is intentionally limited to 48 bits, not stored as a full 64-bit value.
|
||||
// With 49.7 days per 32-bit rollover, the 16-bit counter supports
|
||||
// 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// even when devices run for months. Split into two fields for better memory
|
||||
// alignment on 32-bit systems.
|
||||
uint32_t next_execution_low_; // Lower 32 bits of execution time (millis value)
|
||||
|
||||
@@ -408,8 +408,7 @@ class IDEData:
|
||||
def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
"""Analyze memory usage by component after compilation."""
|
||||
# Lazy import to avoid overhead when not needed
|
||||
from esphome.analyze_memory.cli import MemoryAnalyzerCLI
|
||||
from esphome.analyze_memory.helpers import get_esphome_components
|
||||
from esphome.analyze_memory import MemoryAnalyzer
|
||||
|
||||
idedata = get_idedata(config)
|
||||
|
||||
@@ -436,6 +435,8 @@ def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
external_components = set()
|
||||
|
||||
# Get the list of built-in ESPHome components
|
||||
from esphome.analyze_memory import get_esphome_components
|
||||
|
||||
builtin_components = get_esphome_components()
|
||||
|
||||
# Special non-component keys that appear in configs
|
||||
@@ -456,9 +457,7 @@ def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
_LOGGER.debug("Detected external components: %s", external_components)
|
||||
|
||||
# Create analyzer and run analysis
|
||||
analyzer = MemoryAnalyzerCLI(
|
||||
elf_path, objdump_path, readelf_path, external_components
|
||||
)
|
||||
analyzer = MemoryAnalyzer(elf_path, objdump_path, readelf_path, external_components)
|
||||
analyzer.analyze()
|
||||
|
||||
# Generate and print report
|
||||
|
||||
@@ -4,6 +4,11 @@ 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:
|
||||
@@ -15,3 +20,7 @@ output:
|
||||
gp8403_id: gp8403_10v
|
||||
id: gp8403_output_1
|
||||
channel: 1
|
||||
- platform: gp8403
|
||||
gp8403_id: gp8413
|
||||
id: gp8413_output_2
|
||||
channel: 1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
display:
|
||||
- platform: sdl
|
||||
id: image_display
|
||||
auto_clear_enabled: false
|
||||
dimensions:
|
||||
width: 480
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,11 +13,14 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user