From bd3ecad3a14e57c1bd4284578dd2e1da04a25dc6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 30 Dec 2025 11:51:51 -1000 Subject: [PATCH 01/10] [core] Add format_hex_pretty_to buffer helper and reduce code duplication (#12687) --- esphome/components/hmac_md5/hmac_md5.h | 2 +- esphome/components/hmac_sha256/hmac_sha256.h | 2 +- .../components/zwave_proxy/zwave_proxy.cpp | 6 +- esphome/components/zwave_proxy/zwave_proxy.h | 5 +- esphome/core/hash_base.h | 10 +-- esphome/core/helpers.cpp | 63 +++++++++------ esphome/core/helpers.h | 76 +++++++++++-------- 7 files changed, 96 insertions(+), 68 deletions(-) diff --git a/esphome/components/hmac_md5/hmac_md5.h b/esphome/components/hmac_md5/hmac_md5.h index b83b9d5421..fb9479e3af 100644 --- a/esphome/components/hmac_md5/hmac_md5.h +++ b/esphome/components/hmac_md5/hmac_md5.h @@ -30,7 +30,7 @@ class HmacMD5 { void get_bytes(uint8_t *output); /// Retrieve the HMAC-MD5 digest as hex characters. - /// The output must be able to hold 32 bytes or more. + /// The output must be able to hold 33 bytes or more (32 hex chars + null terminator). void get_hex(char *output); /// Compare the digest against a provided byte-encoded digest (16 bytes). diff --git a/esphome/components/hmac_sha256/hmac_sha256.h b/esphome/components/hmac_sha256/hmac_sha256.h index fa6b64aa94..85622cac46 100644 --- a/esphome/components/hmac_sha256/hmac_sha256.h +++ b/esphome/components/hmac_sha256/hmac_sha256.h @@ -35,7 +35,7 @@ class HmacSHA256 { void get_bytes(uint8_t *output); /// Retrieve the HMAC-SHA256 digest as hex characters. - /// The output must be able to hold 64 bytes or more. + /// The output must be able to hold 65 bytes or more (64 hex chars + null terminator). void get_hex(char *output); /// Compare the digest against a provided byte-encoded digest (32 bytes). diff --git a/esphome/components/zwave_proxy/zwave_proxy.cpp b/esphome/components/zwave_proxy/zwave_proxy.cpp index bd3f85772b..e4efa55e25 100644 --- a/esphome/components/zwave_proxy/zwave_proxy.cpp +++ b/esphome/components/zwave_proxy/zwave_proxy.cpp @@ -123,10 +123,11 @@ void ZWaveProxy::process_uart_() { } void ZWaveProxy::dump_config() { + char hex_buf[format_hex_pretty_size(ZWAVE_HOME_ID_SIZE)]; ESP_LOGCONFIG(TAG, "Z-Wave Proxy:\n" " Home ID: %s", - format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str()); + format_hex_pretty_to(hex_buf, this->home_id_.data(), this->home_id_.size())); } void ZWaveProxy::api_connection_authenticated(api::APIConnection *conn) { @@ -167,7 +168,8 @@ bool ZWaveProxy::set_home_id(const uint8_t *new_home_id) { return false; // No change } std::memcpy(this->home_id_.data(), new_home_id, this->home_id_.size()); - ESP_LOGI(TAG, "Home ID: %s", format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str()); + char hex_buf[format_hex_pretty_size(ZWAVE_HOME_ID_SIZE)]; + ESP_LOGI(TAG, "Home ID: %s", format_hex_pretty_to(hex_buf, this->home_id_.data(), this->home_id_.size())); this->home_id_ready_ = true; return true; // Home ID was changed } diff --git a/esphome/components/zwave_proxy/zwave_proxy.h b/esphome/components/zwave_proxy/zwave_proxy.h index 137a1206e3..f36287d32a 100644 --- a/esphome/components/zwave_proxy/zwave_proxy.h +++ b/esphome/components/zwave_proxy/zwave_proxy.h @@ -14,6 +14,7 @@ namespace esphome::zwave_proxy { static constexpr size_t MAX_ZWAVE_FRAME_SIZE = 257; // Maximum Z-Wave frame size +static constexpr size_t ZWAVE_HOME_ID_SIZE = 4; // Z-Wave Home ID size in bytes enum ZWaveResponseTypes : uint8_t { ZWAVE_FRAME_TYPE_ACK = 0x06, @@ -73,8 +74,8 @@ class ZWaveProxy : public uart::UARTDevice, public Component { // Pre-allocated message - always ready to send api::ZWaveProxyFrame outgoing_proto_msg_; - std::array buffer_; // Fixed buffer for incoming data - std::array home_id_{0, 0, 0, 0}; // Fixed buffer for home ID + std::array buffer_; // Fixed buffer for incoming data + std::array home_id_{}; // Fixed buffer for home ID // Pointers and 32-bit values (aligned together) api::APIConnection *api_connection_{nullptr}; // Current subscribed client diff --git a/esphome/core/hash_base.h b/esphome/core/hash_base.h index c45c4df70b..0c1c2dce33 100644 --- a/esphome/core/hash_base.h +++ b/esphome/core/hash_base.h @@ -25,14 +25,8 @@ class HashBase { /// Retrieve the hash as bytes void get_bytes(uint8_t *output) { memcpy(output, this->digest_, this->get_size()); } - /// Retrieve the hash as hex characters - void get_hex(char *output) { - for (size_t i = 0; i < this->get_size(); i++) { - uint8_t byte = this->digest_[i]; - output[i * 2] = format_hex_char(byte >> 4); - output[i * 2 + 1] = format_hex_char(byte & 0x0F); - } - } + /// Retrieve the hash as hex characters. Output buffer must hold get_size() * 2 + 1 bytes. + void get_hex(char *output) { format_hex_to(output, this->get_size() * 2 + 1, this->digest_, this->get_size()); } /// Compare the hash against a provided byte-encoded hash bool equals_bytes(const uint8_t *expected) { return memcmp(this->digest_, expected, this->get_size()) == 0; } diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 5e361ecce2..1c68f1a021 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -286,43 +286,60 @@ std::string format_mac_address_pretty(const uint8_t *mac) { return std::string(buf); } -std::string format_hex(const uint8_t *data, size_t length) { - std::string ret; - ret.resize(length * 2); - for (size_t i = 0; i < length; i++) { - ret[2 * i] = format_hex_char(data[i] >> 4); - ret[2 * i + 1] = format_hex_char(data[i] & 0x0F); +// Internal helper for hex formatting - base is 'a' for lowercase or 'A' for uppercase +static char *format_hex_internal(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator, + char base) { + if (length == 0) { + buffer[0] = '\0'; + return buffer; + } + // With separator: total length is 3*length (2*length hex chars, (length-1) separators, 1 null terminator) + // Without separator: total length is 2*length + 1 (2*length hex chars, 1 null terminator) + uint8_t stride = separator ? 3 : 2; + size_t max_bytes = separator ? (buffer_size / stride) : ((buffer_size - 1) / stride); + if (max_bytes == 0) { + buffer[0] = '\0'; + return buffer; } - return ret; -} -std::string format_hex(const std::vector &data) { return format_hex(data.data(), data.size()); } - -char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length) { - size_t max_bytes = (buffer_size - 1) / 2; if (length > max_bytes) { length = max_bytes; } for (size_t i = 0; i < length; i++) { - buffer[2 * i] = format_hex_char(data[i] >> 4); - buffer[2 * i + 1] = format_hex_char(data[i] & 0x0F); + size_t pos = i * stride; + buffer[pos] = format_hex_char(data[i] >> 4, base); + buffer[pos + 1] = format_hex_char(data[i] & 0x0F, base); + if (separator && i < length - 1) { + buffer[pos + 2] = separator; + } } - buffer[length * 2] = '\0'; + buffer[length * stride - (separator ? 1 : 0)] = '\0'; return buffer; } +char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length) { + return format_hex_internal(buffer, buffer_size, data, length, 0, 'a'); +} + +std::string format_hex(const uint8_t *data, size_t length) { + std::string ret; + ret.resize(length * 2); + format_hex_to(&ret[0], length * 2 + 1, data, length); + return ret; +} +std::string format_hex(const std::vector &data) { return format_hex(data.data(), data.size()); } + +char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator) { + return format_hex_internal(buffer, buffer_size, data, length, separator, 'A'); +} + // Shared implementation for uint8_t and string hex formatting static std::string format_hex_pretty_uint8(const uint8_t *data, size_t length, char separator, bool show_length) { if (data == nullptr || length == 0) return ""; std::string ret; - uint8_t multiple = separator ? 3 : 2; // 3 if separator is not \0, 2 otherwise - ret.resize(multiple * length - (separator ? 1 : 0)); - for (size_t i = 0; i < length; i++) { - ret[multiple * i] = format_hex_pretty_char(data[i] >> 4); - ret[multiple * i + 1] = format_hex_pretty_char(data[i] & 0x0F); - if (separator && i != length - 1) - ret[multiple * i + 2] = separator; - } + size_t hex_len = separator ? (length * 3 - 1) : (length * 2); + ret.resize(hex_len); + format_hex_pretty_to(&ret[0], hex_len + 1, data, length, separator); if (show_length && length > 4) return ret + " (" + std::to_string(length) + ")"; return ret; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 4319e32510..37534849d0 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -677,12 +677,14 @@ constexpr uint8_t parse_hex_char(char c) { return 255; } +/// Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase) +inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; } + /// Convert a nibble (0-15) to lowercase hex char -inline char format_hex_char(uint8_t v) { return v >= 10 ? 'a' + (v - 10) : '0' + v; } +inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); } /// Convert a nibble (0-15) to uppercase hex char (used for pretty printing) -/// This always uses uppercase (A-F) for pretty/human-readable output -inline char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; } +inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); } /// Write int8 value to buffer without modulo operations. /// Buffer must have at least 4 bytes free. Returns pointer past last char written. @@ -708,28 +710,6 @@ inline char *int8_to_str(char *buf, int8_t val) { return buf; } -/// Format MAC address as XX:XX:XX:XX:XX:XX (uppercase) -inline void format_mac_addr_upper(const uint8_t *mac, char *output) { - for (size_t i = 0; i < 6; i++) { - uint8_t byte = mac[i]; - output[i * 3] = format_hex_pretty_char(byte >> 4); - output[i * 3 + 1] = format_hex_pretty_char(byte & 0x0F); - if (i < 5) - output[i * 3 + 2] = ':'; - } - output[17] = '\0'; -} - -/// Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators) -inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) { - for (size_t i = 0; i < 6; i++) { - uint8_t byte = mac[i]; - output[i * 2] = format_hex_char(byte >> 4); - output[i * 2 + 1] = format_hex_char(byte & 0x0F); - } - output[12] = '\0'; -} - /// Format byte array as lowercase hex to buffer (base implementation). char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length); @@ -748,6 +728,46 @@ inline char *format_hex_to(char (&buffer)[N], T val) { return format_hex_to(buffer, reinterpret_cast(&val), sizeof(T)); } +/// Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0" +constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; } + +/** Format byte array as uppercase hex to buffer (base implementation). + * + * @param buffer Output buffer to write to. + * @param buffer_size Size of the output buffer. + * @param data Pointer to the byte array to format. + * @param length Number of bytes in the array. + * @param separator Character to use between hex bytes, or '\0' for no separator. + * @return Pointer to buffer. + * + * Buffer size needed: length * 3 with separator (for "XX:XX:XX\0"), length * 2 + 1 without. + */ +char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':'); + +/// Format byte array as uppercase hex with separator to buffer. Automatically deduces buffer size. +template +inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') { + static_assert(N >= 3, "Buffer must hold at least one hex byte"); + return format_hex_pretty_to(buffer, N, data, length, separator); +} + +/// MAC address size in bytes +static constexpr size_t MAC_ADDRESS_SIZE = 6; +/// Buffer size for MAC address with separators: "XX:XX:XX:XX:XX:XX\0" +static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE); +/// Buffer size for MAC address without separators: "XXXXXXXXXXXX\0" +static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1; + +/// Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators) +inline void format_mac_addr_upper(const uint8_t *mac, char *output) { + format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':'); +} + +/// Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators) +inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) { + format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE); +} + /// Format the six-byte array \p mac into a MAC address. std::string format_mac_address_pretty(const uint8_t mac[6]); /// Format the byte array \p data of length \p len in lowercased hex. @@ -1203,12 +1223,6 @@ class HighFrequencyLoopRequester { /// Get the device MAC address as raw bytes, written into the provided byte array (6 bytes). void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter) -/// Buffer size for MAC address in lowercase hex notation (12 hex chars + null terminator) -constexpr size_t MAC_ADDRESS_BUFFER_SIZE = 13; - -/// Buffer size for MAC address in colon-separated uppercase hex notation (17 chars + null terminator) -constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = 18; - /// Get the device MAC address as a string, in lowercase hex notation. std::string get_mac_address(); From 98cdef25683c26b42617f1cf65b44c3fb42d8ee2 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Wed, 31 Dec 2025 12:58:37 -0800 Subject: [PATCH 02/10] [hub75] Add clipping check (#12762) --- esphome/components/hub75/hub75.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/hub75/hub75.cpp b/esphome/components/hub75/hub75.cpp index 7317174831..e29f1a898c 100644 --- a/esphome/components/hub75/hub75.cpp +++ b/esphome/components/hub75/hub75.cpp @@ -111,6 +111,9 @@ void HOT HUB75Display::draw_pixel_at(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) [[unlikely]] return; + if (!this->get_clipping().inside(x, y)) + return; + driver_->set_pixel(x, y, color.r, color.g, color.b); App.feed_wdt(); } From 476d00d0e591e10a5ccb41183c649a66291acde9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 31 Dec 2025 10:59:28 -1000 Subject: [PATCH 03/10] [wifi] Fix ESP-IDF reporting connected before DHCP completes on reconnect (#12755) --- esphome/components/wifi/wifi_component_esp_idf.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index b26ac3d2e2..5d4d003d62 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -483,6 +483,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { s_sta_connected = false; s_sta_connect_error = false; s_sta_connect_not_found = false; + // Reset IP address flags - ensures we don't report connected before DHCP completes + // (IP_EVENT_STA_LOST_IP doesn't always fire on disconnect) + this->got_ipv4_address_ = false; +#if USE_NETWORK_IPV6 + this->num_ipv6_addresses_ = 0; +#endif err = esp_wifi_connect(); if (err != ESP_OK) { From 4633803d5dae4d969f00b246d528aee84aab19a8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 31 Dec 2025 11:05:58 -1000 Subject: [PATCH 04/10] [docker] Add build-essential to fix ruamel.yaml 0.19.0 compilation (#12769) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- docker/Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 64ce67e819..348a503bc8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,6 +11,16 @@ FROM base-source-${BUILD_TYPE} AS base RUN git config --system --add safe.directory "*" +# Install build tools for Python packages that require compilation +# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager) +RUN if command -v apk > /dev/null; then \ + apk add --no-cache build-base; \ + else \ + apt-get update \ + && apt-get install -y --no-install-recommends build-essential \ + && rm -rf /var/lib/apt/lists/*; \ + fi + ENV PIP_DISABLE_PIP_VERSION_CHECK=1 RUN pip install --no-cache-dir -U pip uv==0.6.14 From dd855985bec15d235924b9e3bd4a3204a7137221 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Wed, 31 Dec 2025 12:58:37 -0800 Subject: [PATCH 05/10] [hub75] Add clipping check (#12762) --- esphome/components/hub75/hub75.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/hub75/hub75.cpp b/esphome/components/hub75/hub75.cpp index e023e446c4..a09094b87c 100644 --- a/esphome/components/hub75/hub75.cpp +++ b/esphome/components/hub75/hub75.cpp @@ -111,6 +111,9 @@ void HOT HUB75Display::draw_pixel_at(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) [[unlikely]] return; + if (!this->get_clipping().inside(x, y)) + return; + driver_->set_pixel(x, y, color.r, color.g, color.b); App.feed_wdt(); } From f0f01c081ad5498533173b2ba532f3817787e6bb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 31 Dec 2025 10:59:28 -1000 Subject: [PATCH 06/10] [wifi] Fix ESP-IDF reporting connected before DHCP completes on reconnect (#12755) --- esphome/components/wifi/wifi_component_esp_idf.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 380e4ea7fd..fb28018b07 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -483,6 +483,12 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { s_sta_connected = false; s_sta_connect_error = false; s_sta_connect_not_found = false; + // Reset IP address flags - ensures we don't report connected before DHCP completes + // (IP_EVENT_STA_LOST_IP doesn't always fire on disconnect) + this->got_ipv4_address_ = false; +#if USE_NETWORK_IPV6 + this->num_ipv6_addresses_ = 0; +#endif err = esp_wifi_connect(); if (err != ESP_OK) { From 062840dd7bb7aa4e01d5c71b6b85406223cf8e7a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 31 Dec 2025 11:05:58 -1000 Subject: [PATCH 07/10] [docker] Add build-essential to fix ruamel.yaml 0.19.0 compilation (#12769) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- docker/Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 64ce67e819..348a503bc8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -11,6 +11,16 @@ FROM base-source-${BUILD_TYPE} AS base RUN git config --system --add safe.directory "*" +# Install build tools for Python packages that require compilation +# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager) +RUN if command -v apk > /dev/null; then \ + apk add --no-cache build-base; \ + else \ + apt-get update \ + && apt-get install -y --no-install-recommends build-essential \ + && rm -rf /var/lib/apt/lists/*; \ + fi + ENV PIP_DISABLE_PIP_VERSION_CHECK=1 RUN pip install --no-cache-dir -U pip uv==0.6.14 From e9e07129599394a2b42a6594bb1e4bf72f045236 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 31 Dec 2025 16:07:00 -0500 Subject: [PATCH 08/10] Bump version to 2025.12.4 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index fbd5ffa80e..ff74757639 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.12.3 +PROJECT_NUMBER = 2025.12.4 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index ab72bfcaac..3fbdb69215 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.12.3" +__version__ = "2025.12.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1d96de986ec752c19aecbc81aa70f7413d3168fb Mon Sep 17 00:00:00 2001 From: Konstantin Tretyakov <220083+konstantint@users.noreply.github.com> Date: Wed, 31 Dec 2025 22:49:43 +0100 Subject: [PATCH 09/10] [sdist] Include yaml files in components in source distribution package Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 45d5e86672..ed65edc656 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include LICENSE include README.md include requirements.txt +recursive-include esphome *.yaml recursive-include esphome *.cpp *.h *.tcc *.c recursive-include esphome *.py.script recursive-include esphome LICENSE.txt From b1e359750c1a4522a22acea42e1a9afc598a44e6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 31 Dec 2025 16:17:13 -1000 Subject: [PATCH 10/10] [kuntze] Use stack buffer for hex formatting in verbose logging --- esphome/components/kuntze/kuntze.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/kuntze/kuntze.cpp b/esphome/components/kuntze/kuntze.cpp index 30f98aaa99..b2fbeb829b 100644 --- a/esphome/components/kuntze/kuntze.cpp +++ b/esphome/components/kuntze/kuntze.cpp @@ -1,4 +1,5 @@ #include "kuntze.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" #include "esphome/core/application.h" @@ -10,11 +11,17 @@ static const char *const TAG = "kuntze"; static const uint8_t CMD_READ_REG = 0x03; static const uint16_t REGISTER[] = {4136, 4160, 4680, 6000, 4688, 4728, 5832}; +// Maximum bytes to log for Modbus responses (2 registers = 4 bytes, plus byte count = 5 bytes) +static constexpr size_t KUNTZE_MAX_LOG_BYTES = 8; + void Kuntze::on_modbus_data(const std::vector &data) { auto get_16bit = [&](int i) -> uint16_t { return (uint16_t(data[i * 2]) << 8) | uint16_t(data[i * 2 + 1]); }; this->waiting_ = false; - ESP_LOGV(TAG, "Data: %s", format_hex_pretty(data).c_str()); +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + char hex_buf[format_hex_pretty_size(KUNTZE_MAX_LOG_BYTES)]; +#endif + ESP_LOGV(TAG, "Data: %s", format_hex_pretty_to(hex_buf, data.data(), data.size())); float value = (float) get_16bit(0); for (int i = 0; i < data[3]; i++)