From d832ce51cd207f3cea82b04f2a1b7d60c0b68c6e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 15 May 2026 05:42:10 +0200 Subject: [PATCH] [nextion] Replace `connect_info` vector with fixed-size field parser, always log device info (#16059) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- esphome/components/nextion/display.py | 21 ++++++-- esphome/components/nextion/nextion.cpp | 75 +++++++++++++++----------- esphome/components/nextion/nextion.h | 15 +++--- esphome/core/defines.h | 1 - tests/components/nextion/common.yaml | 1 - 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index dc3d5c6d09..89e9b93520 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -1,3 +1,5 @@ +import logging + from esphome import automation import esphome.codegen as cg from esphome.components import display, esp32, uart @@ -39,6 +41,8 @@ from .base_component import ( CONF_WAKE_UP_PAGE, ) +_LOGGER = logging.getLogger(__name__) + CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"] DEPENDENCIES = ["uart"] @@ -55,6 +59,15 @@ NextionSetBrightnessAction = nextion_ns.class_( ) +def _deprecated_dump_device_info(value): + _LOGGER.warning( + "'dump_device_info' is deprecated and will be removed in ESPHome 2026.11.0. " + "Device info is now always logged at connection time. " + "Please remove this option from your configuration." + ) + return value + + def _validate_tft_upload(config): has_tft_url = CONF_TFT_URL in config for conf_key in ( @@ -81,7 +94,10 @@ CONFIG_SCHEMA = cv.All( cv.positive_time_period_milliseconds, cv.Range(max=TimePeriod(milliseconds=255)), ), - cv.Optional(CONF_DUMP_DEVICE_INFO, default=False): cv.boolean, + # Deprecated — device info is now always logged. Remove before 2026.11.0. + cv.Optional(CONF_DUMP_DEVICE_INFO): cv.All( + cv.boolean, _deprecated_dump_device_info + ), cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean, cv.Optional(CONF_MAX_QUEUE_AGE, default="8000ms"): cv.All( cv.positive_time_period_milliseconds, @@ -277,9 +293,6 @@ async def to_code(config): cg.add(var.set_auto_wake_on_touch(config[CONF_AUTO_WAKE_ON_TOUCH])) - if config[CONF_DUMP_DEVICE_INFO]: - cg.add_define("USE_NEXTION_CONFIG_DUMP_DEVICE_INFO") - if config[CONF_EXIT_REPARSE_ON_START]: cg.add_define("USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START") diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index b644cad507..4ebc717552 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -117,30 +117,41 @@ bool Nextion::check_connect_() { ESP_LOGN(TAG, "connect: %s", response.c_str()); - size_t start; + // Parse comok response fields directly + // Format: comok ,,,,,, + size_t field_count = 0; + size_t start = 0; size_t end = 0; - std::vector connect_info; + auto copy_field = [&](char *dst, size_t cap) { + size_t len = (end == std::string::npos ? response.size() : end) - start; + size_t n = len < cap ? len : cap; + std::memcpy(dst, response.data() + start, n); + dst[n] = '\0'; + }; while ((start = response.find_first_not_of(',', end)) != std::string::npos) { end = response.find(',', start); - connect_info.push_back(response.substr(start, end - start)); + switch (field_count) { + case 2: + copy_field(this->device_model_, this->NEXTION_MODEL_MAX); + break; + case 3: + copy_field(this->firmware_version_, this->NEXTION_FW_MAX); + break; + case 5: + copy_field(this->serial_number_, this->NEXTION_SERIAL_MAX); + break; + case 6: + this->flash_size_ = static_cast(std::strtoul(response.data() + start, nullptr, 10)); + break; + default: + break; + } + ++field_count; } - this->is_detected_ = (connect_info.size() == 7); + this->is_detected_ = (field_count == 7); if (this->is_detected_) { - ESP_LOGN(TAG, "Connect info: %zu", connect_info.size()); -#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO - this->device_model_ = connect_info[2]; - this->firmware_version_ = connect_info[3]; - this->serial_number_ = connect_info[5]; - this->flash_size_ = connect_info[6]; -#else // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO - ESP_LOGI(TAG, - " Device Model: %s\n" - " FW Version: %s\n" - " Serial Number: %s\n" - " Flash Size: %s\n", - connect_info[2].c_str(), connect_info[3].c_str(), connect_info[5].c_str(), connect_info[6].c_str()); -#endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO + ESP_LOGN(TAG, "Connect info: %zu fields", field_count); } else { ESP_LOGE(TAG, "Bad connect value: '%s'", response.c_str()); } @@ -178,24 +189,26 @@ void Nextion::dump_config() { #ifdef USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE ESP_LOGCONFIG(TAG, " Skip handshake: YES"); #else // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE -#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO + if (this->is_setup()) { + ESP_LOGCONFIG(TAG, + " Device Model: %s\n" + " FW Version: %s\n" + " Serial Number: %s\n" + " Flash Size: %" PRIu32 " bytes", + this->device_model_, this->firmware_version_, this->serial_number_, this->flash_size_); + } else { + ESP_LOGCONFIG(TAG, " Device info: not yet detected"); + } ESP_LOGCONFIG(TAG, - " Device Model: %s\n" - " FW Version: %s\n" - " Serial Number: %s\n" - " Flash Size: %s\n" - " Max queue age: %u ms\n" - " Startup override: %u ms\n", - this->device_model_.c_str(), this->firmware_version_.c_str(), this->serial_number_.c_str(), - this->flash_size_.c_str(), this->max_q_age_ms_, this->startup_override_ms_); -#endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO #ifdef USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START - ESP_LOGCONFIG(TAG, " Exit reparse: YES\n"); + " Exit reparse: YES\n" #endif // USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START - ESP_LOGCONFIG(TAG, + " Max queue age: %u ms\n" + " Startup override: %u ms\n" " Wake On Touch: %s\n" " Touch Timeout: %" PRIu16, - YESNO(this->connection_state_.auto_wake_on_touch_), this->touch_sleep_timeout_); + this->max_q_age_ms_, this->startup_override_ms_, YESNO(this->connection_state_.auto_wake_on_touch_), + this->touch_sleep_timeout_); #endif // USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE #ifdef USE_NEXTION_MAX_COMMANDS_PER_LOOP diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index c62772ac75..ef030e71da 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1610,12 +1610,15 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe nextion_writer_t writer_; optional brightness_; -#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO - std::string device_model_; - std::string firmware_version_; - std::string serial_number_; - std::string flash_size_; -#endif // USE_NEXTION_CONFIG_DUMP_DEVICE_INFO + // Device info populated from comok response (fixed-size, no heap allocation). + // Sizes derived from Nextion Upload Protocol documentation and observed hardware. + static constexpr size_t NEXTION_MODEL_MAX = 24; ///< Max observed ~18 chars from product numbering rules + static constexpr size_t NEXTION_FW_MAX = 7; ///< 'S' prefix + integer (e.g. 'S99' or `123`) + static constexpr size_t NEXTION_SERIAL_MAX = 20; ///< Consistently 16 hex chars across all documented examples + char device_model_[NEXTION_MODEL_MAX + 1]{}; + char firmware_version_[NEXTION_FW_MAX + 1]{}; + char serial_number_[NEXTION_SERIAL_MAX + 1]{}; + uint32_t flash_size_ = 0; ///< Flash size in bytes — plain integer, no string needed void remove_front_no_sensors_(); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 162a6034b8..ee8e89de8b 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -134,7 +134,6 @@ #define USE_MEDIA_SOURCE #define USE_NEXTION_COMMAND_SPACING #define USE_NEXTION_CONF_START_UP_PAGE -#define USE_NEXTION_CONFIG_DUMP_DEVICE_INFO #define USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START #define USE_NEXTION_CONFIG_SKIP_CONNECTION_HANDSHAKE #define USE_NEXTION_MAX_COMMANDS_PER_LOOP diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index fba6a22b97..d79e3ee2ed 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -276,7 +276,6 @@ display: auto_wake_on_touch: true brightness: 80% command_spacing: 5ms - dump_device_info: true exit_reparse_on_start: true lambda: |- ESP_LOGD("display","Display is being tested!");