From 7ab26a4fe0b5ef4770e76e65a39409be02557375 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:21:58 +1000 Subject: [PATCH 1/3] [ili9xxx][st7735] Add deprecation warnings (#15416) --- esphome/components/ili9xxx/__init__.py | 4 ++++ esphome/components/ili9xxx/display.py | 3 +++ esphome/components/st7735/__init__.py | 5 +++++ esphome/components/st7735/display.py | 6 ++++++ 4 files changed, 18 insertions(+) diff --git a/esphome/components/ili9xxx/__init__.py b/esphome/components/ili9xxx/__init__.py index e69de29bb2..84888bbabc 100644 --- a/esphome/components/ili9xxx/__init__.py +++ b/esphome/components/ili9xxx/__init__.py @@ -0,0 +1,4 @@ +DEPRECATED_COMPONENT = """ +The 'ili9xxx' component is deprecated and no new models will be added to it. +New model PRs should target the newer and more performant 'mipi_spi' component. +""" diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 185a74fa41..1f20b21a0e 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -219,6 +219,9 @@ FINAL_VALIDATE_SCHEMA = final_validate async def to_code(config): + LOGGER.warning( + "The 'ili9xxx' component is deprecated, it is recommended to use 'mipi_spi' instead." + ) rhs = MODELS[config[CONF_MODEL]].new() var = cg.Pvariable(config[CONF_ID], rhs) diff --git a/esphome/components/st7735/__init__.py b/esphome/components/st7735/__init__.py index ba854bb0ae..62dfa6f413 100644 --- a/esphome/components/st7735/__init__.py +++ b/esphome/components/st7735/__init__.py @@ -1,3 +1,8 @@ import esphome.codegen as cg st7735_ns = cg.esphome_ns.namespace("st7735") + +DEPRECATED_COMPONENT = """ +The 'st7735' component is deprecated and no new models will be added to it. +New model PRs should target the newer and more performant 'mipi_spi' component. +""" diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index 9dc69f27ff..766370c21f 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -1,3 +1,5 @@ +import logging + from esphome import pins import esphome.codegen as cg from esphome.components import display, spi @@ -15,6 +17,7 @@ from esphome.const import ( from . import st7735_ns CODEOWNERS = ["@SenexCrenshaw"] +LOGGER = logging.getLogger(__name__) DEPENDENCIES = ["spi"] @@ -87,6 +90,9 @@ async def setup_st7735(var, config): async def to_code(config): + LOGGER.warning( + "The 'st7735' component is deprecated, it is recommended to use 'mipi_spi' instead." + ) var = cg.new_Pvariable( config[CONF_ID], config[CONF_MODEL], From 4f2290d5480afc7e97494ececb7334060e247030 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 16:37:20 -1000 Subject: [PATCH 2/3] [web_server] Disable loop when no SSE clients are connected (#15428) --- esphome/components/web_server/web_server.cpp | 17 +++++++++++++++-- esphome/components/web_server/web_server.h | 3 ++- .../web_server_idf/web_server_idf.cpp | 6 +++++- .../components/web_server_idf/web_server_idf.h | 3 ++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1dda6204fe..a57a8d26ff 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -286,10 +286,11 @@ void DeferredUpdateEventSource::try_send_nodefer(const char *message, const char this->send(message, event, id, reconnect); } -void DeferredUpdateEventSourceList::loop() { +bool DeferredUpdateEventSourceList::loop() { for (DeferredUpdateEventSource *dues : *this) { dues->loop(); } + return !this->empty(); } void DeferredUpdateEventSourceList::deferrable_send_state(void *source, const char *event_type, @@ -318,6 +319,7 @@ void DeferredUpdateEventSourceList::add_new_client(WebServer *ws, AsyncWebServer es->onDisconnect([this, es](AsyncEventSourceClient *client) { this->on_client_disconnect_(es); }); es->handleRequest(request); + ws->enable_loop_soon_any_context(); } void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource *source) { @@ -413,13 +415,24 @@ void WebServer::setup() { // doesn't need defer functionality - if the queue is full, the client JS knows it's alive because it's clearly // getting a lot of events this->set_interval(10000, [this]() { + if (this->events_.empty()) + return; char buf[32]; auto uptime = static_cast(millis_64() / 1000); buf_append_printf(buf, sizeof(buf), 0, "{\"uptime\":%" PRIu32 "}", uptime); this->events_.try_send_nodefer(buf, "ping", millis(), 30000); }); } -void WebServer::loop() { this->events_.loop(); } +void WebServer::loop() { + // No SSE clients connected; stop looping until a new client connects via + // enable_loop_soon_any_context(). This is safe because: + // - set_interval/set_timeout/defer run via the Scheduler, independent of loop() + // - deferrable_send_state early-outs when no clients are connected + // - try_send_nodefer (log, ping) iterates sessions which are empty + // - REST API handlers use defer() which runs via the Scheduler + if (!this->events_.loop()) + this->disable_loop(); +} #ifdef USE_LOGGER void WebServer::on_log(uint8_t level, const char *tag, const char *message, size_t message_len) { diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 6152dfbfd3..8e8b1de8c4 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -169,7 +169,8 @@ class DeferredUpdateEventSourceList final : public std::liston_connect_(rsp); } this->sessions_.push_back(rsp); + // Wake up WebServer::loop() to drain deferred event queues for this client. + // Safe from httpd task context via the pending_enable_loop_ flag. + this->web_server_->enable_loop_soon_any_context(); } -void AsyncEventSource::loop() { +bool AsyncEventSource::loop() { // Clean up dead sessions safely // This follows the ESP-IDF pattern where free_ctx marks resources as dead // and the main loop handles the actual cleanup to avoid race conditions @@ -504,6 +507,7 @@ void AsyncEventSource::loop() { ++i; } } + return !this->sessions_.empty(); } void AsyncEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id, uint32_t reconnect) { diff --git a/esphome/components/web_server_idf/web_server_idf.h b/esphome/components/web_server_idf/web_server_idf.h index 81683e8d85..f2931fb507 100644 --- a/esphome/components/web_server_idf/web_server_idf.h +++ b/esphome/components/web_server_idf/web_server_idf.h @@ -340,7 +340,8 @@ class AsyncEventSource : public AsyncWebHandler { void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0); void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator); - void loop(); + /// Returns true if there are sessions remaining (including pending cleanup). + bool loop(); bool empty() { return this->count() == 0; } size_t count() const { return this->sessions_.size(); } From 2337767c38e09c7e98e0289f2f3cb93a0234c3f8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Apr 2026 16:37:31 -1000 Subject: [PATCH 3/3] [modbus_controller] Fix format specifier warnings (#15429) --- .../components/modbus_controller/modbus_controller.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 3c4ceaf62d..5c3b39c954 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -376,7 +376,7 @@ size_t ModbusController::create_register_ranges_() { while (ix != this->sensorset_.end()) { SensorItem *curr = *ix; - ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count, + ESP_LOGV(TAG, "Register: 0x%X %d %d %zu offset=%u skip=%u addr=%p", curr->start_address, curr->register_count, curr->offset, curr->get_register_size(), curr->offset, curr->skip_updates, curr); if (r.register_count == 0) { @@ -484,18 +484,18 @@ void ModbusController::dump_config() { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGCONFIG(TAG, "sensormap"); for (auto &it : this->sensorset_) { - ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d", + ESP_LOGCONFIG(TAG, " Sensor type=%u start=0x%X offset=0x%X count=%d size=%zu", static_cast(it->register_type), it->start_address, it->offset, it->register_count, it->get_register_size()); } ESP_LOGCONFIG(TAG, "ranges"); for (auto &it : this->register_ranges_) { - ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast(it.register_type), + ESP_LOGCONFIG(TAG, " Range type=%u start=0x%X count=%d skip_updates=%d", static_cast(it.register_type), it.start_address, it.register_count, it.skip_updates); } ESP_LOGCONFIG(TAG, "server registers"); for (auto &r : this->server_registers_) { - ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address, + ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%u register_count=%u", r->address, static_cast(r->value_type), r->register_count); } #endif @@ -524,7 +524,7 @@ void ModbusController::on_write_register_response(ModbusRegisterType register_ty void ModbusController::dump_sensors_() { ESP_LOGV(TAG, "sensors"); for (auto &it : this->sensorset_) { - ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count, + ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%zu offset=%d", it->start_address, it->register_count, it->get_register_size(), it->offset); } }