From a635c8283096859221201d1ba11750c7cb185e97 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:04:50 -1000 Subject: [PATCH 01/30] [pid] Combine log statements to reduce loop blocking (#12942) --- esphome/components/pid/pid_autotuner.cpp | 67 ++++++++++++++---------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/esphome/components/pid/pid_autotuner.cpp b/esphome/components/pid/pid_autotuner.cpp index 28d16e17ab..d1d9c200cf 100644 --- a/esphome/components/pid/pid_autotuner.cpp +++ b/esphome/components/pid/pid_autotuner.cpp @@ -138,20 +138,21 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce } void PIDAutotuner::dump_config() { if (this->state_ == AUTOTUNE_SUCCEEDED) { - ESP_LOGI(TAG, "%s: PID Autotune:", this->id_.c_str()); - ESP_LOGI(TAG, " State: Succeeded!"); + ESP_LOGI(TAG, + "%s: PID Autotune:\n" + " State: Succeeded!", + this->id_.c_str()); bool has_issue = false; if (!this->amplitude_detector_.is_amplitude_convergent()) { - ESP_LOGW(TAG, " Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!"); - ESP_LOGW(TAG, " Please make sure you eliminate all outside influences on the measured temperature."); + ESP_LOGW(TAG, " Could not reliably determine oscillation amplitude, PID parameters may be inaccurate!\n" + " Please make sure you eliminate all outside influences on the measured temperature."); has_issue = true; } if (!this->frequency_detector_.is_increase_decrease_symmetrical()) { - ESP_LOGW(TAG, " Oscillation Frequency is not symmetrical. PID parameters may be inaccurate!"); - ESP_LOGW( - TAG, - " This is usually because the heat and cool processes do not change the temperature at the same rate."); ESP_LOGW(TAG, + " Oscillation Frequency is not symmetrical. PID parameters may be inaccurate!\n" + " This is usually because the heat and cool processes do not change the temperature at the same " + "rate.\n" " Please try reducing the positive_output value (or increase negative_output in case of a cooler)"); has_issue = true; } @@ -160,18 +161,23 @@ void PIDAutotuner::dump_config() { } auto fac = get_ziegler_nichols_pid_(); - ESP_LOGI(TAG, " Calculated PID parameters (\"Ziegler-Nichols PID\" rule):"); - ESP_LOGI(TAG, " "); - ESP_LOGI(TAG, " control_parameters:"); - ESP_LOGI(TAG, " kp: %.5f", fac.kp); - ESP_LOGI(TAG, " ki: %.5f", fac.ki); - ESP_LOGI(TAG, " kd: %.5f", fac.kd); - ESP_LOGI(TAG, " "); - ESP_LOGI(TAG, " Please copy these values into your YAML configuration! They will reset on the next reboot."); + ESP_LOGI(TAG, + " Calculated PID parameters (\"Ziegler-Nichols PID\" rule):\n" + "\n" + " control_parameters:\n" + " kp: %.5f\n" + " ki: %.5f\n" + " kd: %.5f\n" + "\n" + " Please copy these values into your YAML configuration! They will reset on the next reboot.", + fac.kp, fac.ki, fac.kd); - ESP_LOGV(TAG, " Oscillation Period: %f", this->frequency_detector_.get_mean_oscillation_period()); - ESP_LOGV(TAG, " Oscillation Amplitude: %f", this->amplitude_detector_.get_mean_oscillation_amplitude()); - ESP_LOGV(TAG, " Ku: %f, Pu: %f", this->ku_, this->pu_); + ESP_LOGV(TAG, + " Oscillation Period: %f\n" + " Oscillation Amplitude: %f\n" + " Ku: %f, Pu: %f", + this->frequency_detector_.get_mean_oscillation_period(), + this->amplitude_detector_.get_mean_oscillation_amplitude(), this->ku_, this->pu_); ESP_LOGD(TAG, " Alternative Rules:"); // http://www.mstarlabs.com/control/znrule.html @@ -183,13 +189,16 @@ void PIDAutotuner::dump_config() { } if (this->state_ == AUTOTUNE_RUNNING) { - ESP_LOGD(TAG, "%s: PID Autotune:", this->id_.c_str()); - ESP_LOGD(TAG, " Autotune is still running!"); - ESP_LOGD(TAG, " Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error()); - ESP_LOGD(TAG, " Stats so far:"); - ESP_LOGD(TAG, " Phases: %" PRIu32, relay_function_.phase_count); - ESP_LOGD(TAG, " Detected %zu zero-crossings", frequency_detector_.zerocrossing_intervals.size()); - ESP_LOGD(TAG, " Current Phase Min: %.2f, Max: %.2f", amplitude_detector_.phase_min, + ESP_LOGD(TAG, + "%s: PID Autotune:\n" + " Autotune is still running!\n" + " Status: Trying to reach %.2f °C\n" + " Stats so far:\n" + " Phases: %" PRIu32 "\n" + " Detected %zu zero-crossings\n" + " Current Phase Min: %.2f, Max: %.2f", + this->id_.c_str(), setpoint_ - relay_function_.current_target_error(), relay_function_.phase_count, + frequency_detector_.zerocrossing_intervals.size(), amplitude_detector_.phase_min, amplitude_detector_.phase_max); } } @@ -205,8 +214,10 @@ PIDAutotuner::PIDResult PIDAutotuner::calculate_pid_(float kp_factor, float ki_f } void PIDAutotuner::print_rule_(const char *name, float kp_factor, float ki_factor, float kd_factor) { auto fac = calculate_pid_(kp_factor, ki_factor, kd_factor); - ESP_LOGD(TAG, " Rule '%s':", name); - ESP_LOGD(TAG, " kp: %.5f, ki: %.5f, kd: %.5f", fac.kp, fac.ki, fac.kd); + ESP_LOGD(TAG, + " Rule '%s':\n" + " kp: %.5f, ki: %.5f, kd: %.5f", + name, fac.kp, fac.ki, fac.kd); } // ================== RelayFunction ================== From 3c8fd5c5c0715740e4ef3e346f541c7b9b3d3694 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:05:05 -1000 Subject: [PATCH 02/30] [pulse_counter] Combine log statements to reduce loop blocking (#12946) --- esphome/components/pulse_counter/pulse_counter_sensor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 6300d6fe96..c0d74cef4a 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -68,8 +68,10 @@ bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { next_pcnt_channel = pcnt_channel_t(int(next_pcnt_channel) + 1); } - ESP_LOGCONFIG(TAG, " PCNT Unit Number: %u", this->pcnt_unit); - ESP_LOGCONFIG(TAG, " PCNT Channel Number: %u", this->pcnt_channel); + ESP_LOGCONFIG(TAG, + " PCNT Unit Number: %u\n" + " PCNT Channel Number: %u", + this->pcnt_unit, this->pcnt_channel); pcnt_count_mode_t rising = PCNT_COUNT_DIS, falling = PCNT_COUNT_DIS; switch (this->rising_edge_mode) { From e6a630ae647cd3dc60f30023ca5452d569097d0a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:06:34 -1000 Subject: [PATCH 03/30] [qmp6988] Combine log statements to reduce loop blocking (#12947) --- esphome/components/qmp6988/qmp6988.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/esphome/components/qmp6988/qmp6988.cpp b/esphome/components/qmp6988/qmp6988.cpp index 57f54b6432..4e1ef27d5e 100644 --- a/esphome/components/qmp6988/qmp6988.cpp +++ b/esphome/components/qmp6988/qmp6988.cpp @@ -127,9 +127,11 @@ bool QMP6988Component::get_calibration_data_() { qmp6988_data_.qmp6988_cali.COE_b21 = (int16_t) encode_uint16(a_data_uint8_tr[14], a_data_uint8_tr[15]); qmp6988_data_.qmp6988_cali.COE_bp3 = (int16_t) encode_uint16(a_data_uint8_tr[16], a_data_uint8_tr[17]); - ESP_LOGV(TAG, "<-----------calibration data-------------->\r\n"); - ESP_LOGV(TAG, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0, - qmp6988_data_.qmp6988_cali.COE_a1, qmp6988_data_.qmp6988_cali.COE_a2, qmp6988_data_.qmp6988_cali.COE_b00); + ESP_LOGV(TAG, + "<-----------calibration data-------------->\n" + "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]", + qmp6988_data_.qmp6988_cali.COE_a0, qmp6988_data_.qmp6988_cali.COE_a1, qmp6988_data_.qmp6988_cali.COE_a2, + qmp6988_data_.qmp6988_cali.COE_b00); ESP_LOGV(TAG, "COE_bt1[%d] COE_bt2[%d] COE_bp1[%d] COE_b11[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_bt1, qmp6988_data_.qmp6988_cali.COE_bt2, qmp6988_data_.qmp6988_cali.COE_bp1, qmp6988_data_.qmp6988_cali.COE_b11); ESP_LOGV(TAG, "COE_bp2[%d] COE_b12[%d] COE_b21[%d] COE_bp3[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_bp2, @@ -150,9 +152,10 @@ bool QMP6988Component::get_calibration_data_() { qmp6988_data_.ik.b12 = 6846L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b12 + 85590281L; // 29Q53 qmp6988_data_.ik.b21 = 13836L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b21 + 79333336L; // 29Q60 qmp6988_data_.ik.bp3 = 2915L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp3 + 157155561L; // 28Q65 - ESP_LOGV(TAG, "<----------- int calibration data -------------->\r\n"); - ESP_LOGV(TAG, "a0[%d] a1[%d] a2[%d] b00[%d]\r\n", qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2, - qmp6988_data_.ik.b00); + ESP_LOGV(TAG, + "<----------- int calibration data -------------->\n" + "a0[%d] a1[%d] a2[%d] b00[%d]", + qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2, qmp6988_data_.ik.b00); ESP_LOGV(TAG, "bt1[%lld] bt2[%lld] bp1[%lld] b11[%lld]\r\n", qmp6988_data_.ik.bt1, qmp6988_data_.ik.bt2, qmp6988_data_.ik.bp1, qmp6988_data_.ik.b11); ESP_LOGV(TAG, "bp2[%lld] b12[%lld] b21[%lld] bp3[%lld]\r\n", qmp6988_data_.ik.bp2, qmp6988_data_.ik.b12, @@ -330,8 +333,10 @@ void QMP6988Component::dump_config() { LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); ESP_LOGCONFIG(TAG, " Temperature Oversampling: %s", oversampling_to_str(this->temperature_oversampling_)); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); - ESP_LOGCONFIG(TAG, " Pressure Oversampling: %s", oversampling_to_str(this->pressure_oversampling_)); - ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_)); + ESP_LOGCONFIG(TAG, + " Pressure Oversampling: %s\n" + " IIR Filter: %s", + oversampling_to_str(this->pressure_oversampling_), iir_filter_to_str(this->iir_filter_)); } void QMP6988Component::update() { From 3ec05a5a13f96855b7c1ba1c207a453dba1992c1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:06:55 -1000 Subject: [PATCH 04/30] [radon_eye_rd200] Combine log statements to reduce loop blocking (#12949) --- esphome/components/radon_eye_rd200/radon_eye_rd200.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp b/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp index 3959178b94..3ccb7bf082 100644 --- a/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp +++ b/esphome/components/radon_eye_rd200/radon_eye_rd200.cpp @@ -118,10 +118,11 @@ void RadonEyeRD200::read_sensors_(uint8_t *value, uint16_t value_len) { radon_long_term_sensor_->publish_state(radon_day); } - ESP_LOGV(TAG, " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f", radon_now, radon_day, radon_month); - - ESP_LOGV(TAG, " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f", radon_now / convert_to_bwpm3, - radon_day / convert_to_bwpm3, radon_month / convert_to_bwpm3); + ESP_LOGV(TAG, + " Measurements (Bq/m³) now: %0.03f, day: %0.03f, month: %0.03f\n" + " Measurements (pCi/L) now: %0.03f, day: %0.03f, month: %0.03f", + radon_now, radon_day, radon_month, radon_now / convert_to_bwpm3, radon_day / convert_to_bwpm3, + radon_month / convert_to_bwpm3); // This instance must not stay connected // so other clients can connect to it (e.g. the From 44fc156ef6f238b1f9053c719d5f98b8a0df6647 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:07:50 -1000 Subject: [PATCH 05/30] [remote_base] Combine log statements to reduce loop blocking (#12950) --- esphome/components/remote_base/pronto_protocol.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 9fbc9e85ba..401a0976b2 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -104,8 +104,10 @@ void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::vector Date: Sun, 4 Jan 2026 16:08:45 -1000 Subject: [PATCH 06/30] [remote_receiver] Combine log statements to reduce loop blocking (#12951) --- esphome/components/remote_receiver/remote_receiver.cpp | 4 ++-- esphome/components/remote_receiver/remote_receiver_esp32.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/remote_receiver/remote_receiver.cpp b/esphome/components/remote_receiver/remote_receiver.cpp index a7ac74199d..de47457dac 100644 --- a/esphome/components/remote_receiver/remote_receiver.cpp +++ b/esphome/components/remote_receiver/remote_receiver.cpp @@ -76,9 +76,8 @@ void RemoteReceiverComponent::setup() { } void RemoteReceiverComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Receiver:"); - LOG_PIN(" Pin: ", this->pin_); ESP_LOGCONFIG(TAG, + "Remote Receiver:\n" " Buffer Size: %u\n" " Tolerance: %u%s\n" " Filter out pulses shorter than: %u us\n" @@ -86,6 +85,7 @@ void RemoteReceiverComponent::dump_config() { this->buffer_size_, this->tolerance_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, this->idle_us_); + LOG_PIN(" Pin: ", this->pin_); } void RemoteReceiverComponent::loop() { diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index bd0bc8e57b..eda8365169 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -117,9 +117,8 @@ void RemoteReceiverComponent::setup() { } void RemoteReceiverComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Receiver:"); - LOG_PIN(" Pin: ", this->pin_); ESP_LOGCONFIG(TAG, + "Remote Receiver:\n" " Clock resolution: %" PRIu32 " hz\n" " RMT symbols: %" PRIu32 "\n" " Filter symbols: %" PRIu32 "\n" @@ -132,6 +131,7 @@ void RemoteReceiverComponent::dump_config() { this->clock_resolution_, this->rmt_symbols_, this->filter_symbols_, this->receive_symbols_, this->tolerance_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->carrier_frequency_, this->carrier_duty_percent_, this->filter_us_, this->idle_us_); + LOG_PIN(" Pin: ", this->pin_); if (this->is_failed()) { ESP_LOGE(TAG, "Configuring RMT driver failed: %s (%s)", esp_err_to_name(this->error_code_), this->error_string_.c_str()); From 4f20c1ceb17aad02d8c5aec2dfbbb1f00cf5ffe9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:09:51 -1000 Subject: [PATCH 07/30] [rp2040_pwm] Combine log statements to reduce loop blocking (#12952) --- esphome/components/rp2040_pwm/rp2040_pwm.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/rp2040_pwm/rp2040_pwm.cpp b/esphome/components/rp2040_pwm/rp2040_pwm.cpp index ec164b3c05..90a507b14f 100644 --- a/esphome/components/rp2040_pwm/rp2040_pwm.cpp +++ b/esphome/components/rp2040_pwm/rp2040_pwm.cpp @@ -36,9 +36,11 @@ void RP2040PWM::setup_pwm_() { } void RP2040PWM::dump_config() { - ESP_LOGCONFIG(TAG, "RP2040 PWM:"); + ESP_LOGCONFIG(TAG, + "RP2040 PWM:\n" + " Frequency: %.1f Hz", + this->frequency_); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_); LOG_FLOAT_OUTPUT(this); } void HOT RP2040PWM::write_state(float state) { From 7449421cea5b8fa76dfa5e713bdaae30a4850847 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:10:06 -1000 Subject: [PATCH 08/30] [shelly_dimmer] Combine log statements to reduce loop blocking (#12956) --- .../shelly_dimmer/shelly_dimmer.cpp | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index 3b5307805e..bdb33d31af 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -113,26 +113,20 @@ void ShellyDimmer::setup() { void ShellyDimmer::update() { this->send_command_(SHELLY_DIMMER_PROTO_CMD_POLL, nullptr, 0); } void ShellyDimmer::dump_config() { - ESP_LOGCONFIG(TAG, "ShellyDimmer:"); - LOG_PIN(" NRST Pin: ", this->pin_nrst_); - LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_); - ESP_LOGCONFIG(TAG, + "ShellyDimmer:\n" " Leading Edge: %s\n" " Warmup Brightness: %d\n" " Minimum Brightness: %d\n" - " Maximum Brightness: %d", - YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_); - // ESP_LOGCONFIG(TAG, " Warmup Time: %d", this->warmup_time_); - // ESP_LOGCONFIG(TAG, " Fade Rate: %d", this->fade_rate_); - - LOG_UPDATE_INTERVAL(this); - - ESP_LOGCONFIG(TAG, - " STM32 current firmware version: %d.%d \n" + " Maximum Brightness: %d\n" + " STM32 current firmware version: %d.%d\n" " STM32 required firmware version: %d.%d", + YESNO(this->leading_edge_), this->warmup_brightness_, this->min_brightness_, this->max_brightness_, this->version_major_, this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); + LOG_PIN(" NRST Pin: ", this->pin_nrst_); + LOG_PIN(" BOOT0 Pin: ", this->pin_boot0_); + LOG_UPDATE_INTERVAL(this); if (this->version_major_ != USE_SHD_FIRMWARE_MAJOR_VERSION || this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) { @@ -439,13 +433,15 @@ bool ShellyDimmer::handle_frame_() { current = CURRENT_SCALING_FACTOR / static_cast(current_raw); } - ESP_LOGI(TAG, "Got dimmer data:"); - ESP_LOGI(TAG, " HW version: %d", hw_version); - ESP_LOGI(TAG, " Brightness: %d", brightness); - ESP_LOGI(TAG, " Fade rate: %d", fade_rate); - ESP_LOGI(TAG, " Power: %f W", power); - ESP_LOGI(TAG, " Voltage: %f V", voltage); - ESP_LOGI(TAG, " Current: %f A", current); + ESP_LOGI(TAG, + "Got dimmer data:\n" + " HW version: %d\n" + " Brightness: %d\n" + " Fade rate: %d\n" + " Power: %f W\n" + " Voltage: %f V\n" + " Current: %f A", + hw_version, brightness, fade_rate, power, voltage, current); // Update sensors. if (this->power_sensor_ != nullptr) { From 9f7925c1d589b54fa857bd9b379d9fbbc9cc82cc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:10:19 -1000 Subject: [PATCH 09/30] [safe_mode] Combine log statements to reduce loop blocking (#12955) --- esphome/components/safe_mode/safe_mode.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp index c933222273..c7bd8748f5 100644 --- a/esphome/components/safe_mode/safe_mode.cpp +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -40,8 +40,10 @@ void SafeModeComponent::dump_config() { #ifdef USE_OTA_ROLLBACK const esp_partition_t *last_invalid = esp_ota_get_last_invalid_partition(); if (last_invalid != nullptr) { - ESP_LOGW(TAG, "OTA rollback detected! Rolled back from partition '%s'", last_invalid->label); - ESP_LOGW(TAG, "The device reset before the boot was marked successful"); + ESP_LOGW(TAG, + "OTA rollback detected! Rolled back from partition '%s'\n" + "The device reset before the boot was marked successful", + last_invalid->label); } #endif } From ab0e15e4bbd6f31371e280395ab9b1c14ab8c948 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:10:51 -1000 Subject: [PATCH 10/30] [runtime_stats] Combine log statements to reduce loop blocking (#12954) --- esphome/components/runtime_stats/runtime_stats.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/runtime_stats/runtime_stats.cpp b/esphome/components/runtime_stats/runtime_stats.cpp index f95be5291f..7e837a18e8 100644 --- a/esphome/components/runtime_stats/runtime_stats.cpp +++ b/esphome/components/runtime_stats/runtime_stats.cpp @@ -27,8 +27,10 @@ void RuntimeStatsCollector::record_component_time(Component *component, uint32_t } void RuntimeStatsCollector::log_stats_() { - ESP_LOGI(TAG, "Component Runtime Statistics"); - ESP_LOGI(TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_); + ESP_LOGI(TAG, + "Component Runtime Statistics\n" + "Period stats (last %" PRIu32 "ms):", + this->log_interval_); // First collect stats we want to display std::vector stats_to_display; From 12027569d33e7069bca7ed7986ba332ae3f55617 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 5 Jan 2026 03:11:14 +0100 Subject: [PATCH 11/30] [nrf52,zigbee] add support for binary_input (#11535) Co-authored-by: J. Nick Koston Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: J. Nick Koston Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: J. Nick Koston --- CODEOWNERS | 1 + esphome/components/binary_sensor/__init__.py | 6 +- esphome/components/zephyr/core.cpp | 5 + esphome/components/zigbee/__init__.py | 124 ++++++++ esphome/components/zigbee/automation.h | 16 ++ esphome/components/zigbee/const_zephyr.py | 24 ++ .../zigbee/zigbee_binary_sensor_zephyr.cpp | 37 +++ .../zigbee/zigbee_binary_sensor_zephyr.h | 45 +++ esphome/components/zigbee/zigbee_zephyr.cpp | 190 +++++++++++++ esphome/components/zigbee/zigbee_zephyr.h | 104 +++++++ esphome/components/zigbee/zigbee_zephyr.py | 265 ++++++++++++++++++ esphome/core/defines.h | 3 + script/helpers_zephyr.py | 12 +- tests/components/zigbee/common.yaml | 34 +++ .../zigbee/test.nrf52-adafruit.yaml | 1 + .../components/zigbee/test.nrf52-mcumgr.yaml | 1 + .../zigbee/test.nrf52-xiao-ble.yaml | 1 + 17 files changed, 866 insertions(+), 3 deletions(-) create mode 100644 esphome/components/zigbee/__init__.py create mode 100644 esphome/components/zigbee/automation.h create mode 100644 esphome/components/zigbee/const_zephyr.py create mode 100644 esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp create mode 100644 esphome/components/zigbee/zigbee_binary_sensor_zephyr.h create mode 100644 esphome/components/zigbee/zigbee_zephyr.cpp create mode 100644 esphome/components/zigbee/zigbee_zephyr.h create mode 100644 esphome/components/zigbee/zigbee_zephyr.py create mode 100644 tests/components/zigbee/common.yaml create mode 100644 tests/components/zigbee/test.nrf52-adafruit.yaml create mode 100644 tests/components/zigbee/test.nrf52-mcumgr.yaml create mode 100644 tests/components/zigbee/test.nrf52-xiao-ble.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 0d9396aa6f..00db5a3c79 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -575,5 +575,6 @@ esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 esphome/components/xxtea/* @clydebarrow esphome/components/zephyr/* @tomaszduda23 esphome/components/zhlt01/* @cfeenstra1024 +esphome/components/zigbee/* @tomaszduda23 esphome/components/zio_ultrasonic/* @kahrendt esphome/components/zwave_proxy/* @kbx81 diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index cbf935a501..c38d6b78d3 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -3,7 +3,7 @@ from logging import getLogger from esphome import automation, core from esphome.automation import Condition, maybe_simple_id import esphome.codegen as cg -from esphome.components import mqtt, web_server +from esphome.components import mqtt, web_server, zigbee from esphome.components.const import CONF_ON_STATE_CHANGE import esphome.config_validation as cv from esphome.const import ( @@ -439,6 +439,7 @@ def validate_publish_initial_state(value): _BINARY_SENSOR_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMPONENT_SCHEMA) + .extend(zigbee.BINARY_SENSOR_SCHEMA) .extend( { cv.GenerateID(): cv.declare_id(BinarySensor), @@ -520,6 +521,7 @@ _BINARY_SENSOR_SCHEMA = ( _BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor")) +_BINARY_SENSOR_SCHEMA.add_extra(zigbee.validate_binary_sensor) def binary_sensor_schema( @@ -621,6 +623,8 @@ async def setup_binary_sensor_core_(var, config): if web_server_config := config.get(CONF_WEB_SERVER): await web_server.add_entity_config(var, web_server_config) + await zigbee.setup_binary_sensor(var, config) + async def register_binary_sensor(var, config): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/components/zephyr/core.cpp b/esphome/components/zephyr/core.cpp index 46589cdb62..d7027b33f5 100644 --- a/esphome/components/zephyr/core.cpp +++ b/esphome/components/zephyr/core.cpp @@ -26,7 +26,12 @@ void arch_init() { if (device_is_ready(WDT)) { static wdt_timeout_cfg wdt_config{}; wdt_config.flags = WDT_FLAG_RESET_SOC; +#ifdef USE_ZIGBEE + // zboss thread use a lot of cpu cycles during start + wdt_config.window.max = 10000; +#else wdt_config.window.max = 2000; +#endif wdt_channel_id = wdt_install_timeout(WDT, &wdt_config); if (wdt_channel_id >= 0) { uint8_t options = 0; diff --git a/esphome/components/zigbee/__init__.py b/esphome/components/zigbee/__init__.py new file mode 100644 index 0000000000..2009f92d2e --- /dev/null +++ b/esphome/components/zigbee/__init__.py @@ -0,0 +1,124 @@ +from typing import Any + +from esphome import automation, core +import esphome.codegen as cg +from esphome.components.nrf52.boards import BOOTLOADER_CONFIG, Section +from esphome.components.zephyr import zephyr_add_pm_static, zephyr_data +from esphome.components.zephyr.const import KEY_BOOTLOADER +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERNAL +from esphome.core import CORE +from esphome.types import ConfigType + +from .const_zephyr import ( + CONF_MAX_EP_NUMBER, + CONF_ON_JOIN, + CONF_WIPE_ON_BOOT, + CONF_ZIGBEE_ID, + KEY_EP_NUMBER, + KEY_ZIGBEE, + ZigbeeComponent, + zigbee_ns, +) +from .zigbee_zephyr import zephyr_binary_sensor + +CODEOWNERS = ["@tomaszduda23"] + + +def zigbee_set_core_data(config: ConfigType) -> ConfigType: + if zephyr_data()[KEY_BOOTLOADER] in BOOTLOADER_CONFIG: + zephyr_add_pm_static( + [Section("empty_after_zboss_offset", 0xF4000, 0xC000, "flash_primary")] + ) + + return config + + +BINARY_SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_binary_sensor) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(ZigbeeComponent), + cv.Optional(CONF_ON_JOIN): automation.validate_automation(single=True), + cv.Optional(CONF_WIPE_ON_BOOT, default=False): cv.All( + cv.boolean, + cv.requires_component("nrf52"), + ), + } + ).extend(cv.COMPONENT_SCHEMA), + zigbee_set_core_data, + cv.only_with_framework("zephyr"), +) + + +def validate_number_of_ep(config: ConfigType) -> None: + if KEY_ZIGBEE not in CORE.data: + raise cv.Invalid("At least one zigbee device need to be included") + count = len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]) + if count == 1: + raise cv.Invalid( + "Single endpoint is not supported https://github.com/Koenkk/zigbee2mqtt/issues/29888" + ) + if count > CONF_MAX_EP_NUMBER: + raise cv.Invalid(f"Maximum number of end points is {CONF_MAX_EP_NUMBER}") + + +FINAL_VALIDATE_SCHEMA = cv.All( + validate_number_of_ep, +) + + +async def to_code(config: ConfigType) -> None: + cg.add_define("USE_ZIGBEE") + if CORE.using_zephyr: + from .zigbee_zephyr import zephyr_to_code + + await zephyr_to_code(config) + + +async def setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None: + if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL): + return + if CORE.using_zephyr: + from .zigbee_zephyr import zephyr_setup_binary_sensor + + await zephyr_setup_binary_sensor(entity, config) + + +def validate_binary_sensor(config: ConfigType) -> ConfigType: + if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL): + return config + data: dict[str, Any] = CORE.data.setdefault(KEY_ZIGBEE, {}) + slots: list[str] = data.setdefault(KEY_EP_NUMBER, []) + slots.extend([""]) + return config + + +ZIGBEE_ACTION_SCHEMA = automation.maybe_simple_id( + cv.Schema( + { + cv.GenerateID(): cv.use_id(ZigbeeComponent), + } + ) +) + +FactoryResetAction = zigbee_ns.class_( + "FactoryResetAction", automation.Action, cg.Parented.template(ZigbeeComponent) +) + + +@automation.register_action( + "zigbee.factory_reset", + FactoryResetAction, + ZIGBEE_ACTION_SCHEMA, +) +async def reset_zigbee_to_code( + config: ConfigType, + action_id: core.ID, + template_arg: cg.TemplateArguments, + args: list[tuple], +) -> cg.Pvariable: + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/zigbee/automation.h b/esphome/components/zigbee/automation.h new file mode 100644 index 0000000000..1822e6a029 --- /dev/null +++ b/esphome/components/zigbee/automation.h @@ -0,0 +1,16 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_ZIGBEE +#ifdef USE_NRF52 +#include "zigbee_zephyr.h" +#endif +namespace esphome::zigbee { + +template class FactoryResetAction : public Action, public Parented { + public: + void play(const Ts &...x) override { this->parent_->factory_reset(); } +}; + +} // namespace esphome::zigbee + +#endif diff --git a/esphome/components/zigbee/const_zephyr.py b/esphome/components/zigbee/const_zephyr.py new file mode 100644 index 0000000000..ecd08f1f0a --- /dev/null +++ b/esphome/components/zigbee/const_zephyr.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg + +zigbee_ns = cg.esphome_ns.namespace("zigbee") +ZigbeeComponent = zigbee_ns.class_("ZigbeeComponent", cg.Component) +BinaryAttrs = zigbee_ns.struct("BinaryAttrs") + +CONF_MAX_EP_NUMBER = 8 +CONF_ZIGBEE_ID = "zigbee_id" +CONF_ON_JOIN = "on_join" +CONF_WIPE_ON_BOOT = "wipe_on_boot" +CONF_ZIGBEE_BINARY_SENSOR = "zigbee_binary_sensor" + +# Keys for CORE.data storage +KEY_ZIGBEE = "zigbee" +KEY_EP_NUMBER = "ep_number" + +# External ZBOSS SDK types (just strings for codegen) +ZB_ZCL_BASIC_ATTRS_EXT_T = "zb_zcl_basic_attrs_ext_t" +ZB_ZCL_IDENTIFY_ATTRS_T = "zb_zcl_identify_attrs_t" + +# Cluster IDs +ZB_ZCL_CLUSTER_ID_BASIC = "ZB_ZCL_CLUSTER_ID_BASIC" +ZB_ZCL_CLUSTER_ID_IDENTIFY = "ZB_ZCL_CLUSTER_ID_IDENTIFY" +ZB_ZCL_CLUSTER_ID_BINARY_INPUT = "ZB_ZCL_CLUSTER_ID_BINARY_INPUT" diff --git a/esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp b/esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp new file mode 100644 index 0000000000..744d04adc5 --- /dev/null +++ b/esphome/components/zigbee/zigbee_binary_sensor_zephyr.cpp @@ -0,0 +1,37 @@ +#include "zigbee_binary_sensor_zephyr.h" +#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_BINARY_SENSOR) +#include "esphome/core/log.h" +extern "C" { +#include +#include +#include +#include +#include +} +namespace esphome::zigbee { + +static const char *const TAG = "zigbee.binary_sensor"; + +ZigbeeBinarySensor::ZigbeeBinarySensor(binary_sensor::BinarySensor *binary_sensor) : binary_sensor_(binary_sensor) {} + +void ZigbeeBinarySensor::setup() { + this->binary_sensor_->add_on_state_callback([this](bool state) { + this->cluster_attributes_->present_value = state ? ZB_TRUE : ZB_FALSE; + ESP_LOGD(TAG, "Set attribute end point: %d, present_value %d", this->end_point_, + this->cluster_attributes_->present_value); + ZB_ZCL_SET_ATTRIBUTE(this->end_point_, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE, + ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &this->cluster_attributes_->present_value, + ZB_FALSE); + this->parent_->flush(); + }); +} + +void ZigbeeBinarySensor::dump_config() { + ESP_LOGCONFIG(TAG, + "Zigbee Binary Sensor\n" + " End point: %d, present_value %u", + this->end_point_, this->cluster_attributes_->present_value); +} + +} // namespace esphome::zigbee +#endif diff --git a/esphome/components/zigbee/zigbee_binary_sensor_zephyr.h b/esphome/components/zigbee/zigbee_binary_sensor_zephyr.h new file mode 100644 index 0000000000..aae79fa289 --- /dev/null +++ b/esphome/components/zigbee/zigbee_binary_sensor_zephyr.h @@ -0,0 +1,45 @@ +#pragma once +#include "esphome/core/defines.h" +#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_BINARY_SENSOR) +#include "esphome/components/zigbee/zigbee_zephyr.h" +#include "esphome/core/component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +extern "C" { +#include +#include +} + +// it should have been defined inside of sdk. It is missing though +#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID(data_ptr) \ + { \ + ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, ZB_ZCL_ATTR_TYPE_CHAR_STRING, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \ + (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \ + } + +// copy of ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST + description +#define ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \ + description) \ + ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_BINARY_INPUT) \ + ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_OUT_OF_SERVICE_ID, (out_of_service)) \ + ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, (present_value)) \ + ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_STATUS_FLAG_ID, (status_flag)) \ + ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (description)) \ + ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST + +namespace esphome::zigbee { + +class ZigbeeBinarySensor : public ZigbeeEntity, public Component { + public: + explicit ZigbeeBinarySensor(binary_sensor::BinarySensor *binary_sensor); + void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; } + + void setup() override; + void dump_config() override; + + protected: + BinaryAttrs *cluster_attributes_{nullptr}; + binary_sensor::BinarySensor *binary_sensor_; +}; + +} // namespace esphome::zigbee +#endif diff --git a/esphome/components/zigbee/zigbee_zephyr.cpp b/esphome/components/zigbee/zigbee_zephyr.cpp new file mode 100644 index 0000000000..c9027d0a74 --- /dev/null +++ b/esphome/components/zigbee/zigbee_zephyr.cpp @@ -0,0 +1,190 @@ +#include "zigbee_zephyr.h" +#if defined(USE_ZIGBEE) && defined(USE_NRF52) +#include "esphome/core/log.h" +#include +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +namespace esphome::zigbee { + +static const char *const TAG = "zigbee"; + +ZigbeeComponent *global_zigbee = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +const uint8_t IEEE_ADDR_BUF_SIZE = 17; + +void ZigbeeComponent::zboss_signal_handler_esphome(zb_bufid_t bufid) { + zb_zdo_app_signal_hdr_t *sig_hndler = nullptr; + zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, &sig_hndler); + zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid); + + switch (sig) { + case ZB_ZDO_SIGNAL_SKIP_STARTUP: + ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_SKIP_STARTUP, status: %d", status); + break; + case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY: + ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY, status: %d", status); + break; + case ZB_ZDO_SIGNAL_LEAVE: + ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_LEAVE, status: %d", status); + break; + case ZB_BDB_SIGNAL_DEVICE_REBOOT: + ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_REBOOT, status: %d", status); + if (status == RET_OK) { + on_join_(); + } + break; + case ZB_BDB_SIGNAL_STEERING: + break; + case ZB_COMMON_SIGNAL_CAN_SLEEP: + ESP_LOGV(TAG, "ZB_COMMON_SIGNAL_CAN_SLEEP, status: %d", status); + break; + case ZB_BDB_SIGNAL_DEVICE_FIRST_START: + ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_FIRST_START, status: %d", status); + break; + case ZB_NLME_STATUS_INDICATION: + ESP_LOGD(TAG, "ZB_NLME_STATUS_INDICATION, status: %d", status); + break; + case ZB_BDB_SIGNAL_TC_REJOIN_DONE: + ESP_LOGD(TAG, "ZB_BDB_SIGNAL_TC_REJOIN_DONE, status: %d", status); + break; + default: + ESP_LOGD(TAG, "zboss_signal_handler sig: %d, status: %d", sig, status); + break; + } + + auto err = zigbee_default_signal_handler(bufid); + if (err != RET_OK) { + ESP_LOGE(TAG, "Zigbee_default_signal_handler ERROR %u [%s]", err, zb_error_to_string_get(err)); + } + + switch (sig) { + case ZB_BDB_SIGNAL_STEERING: + ESP_LOGD(TAG, "ZB_BDB_SIGNAL_STEERING, status: %d", status); + if (status == RET_OK) { + zb_ext_pan_id_t extended_pan_id; + char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0}; + int addr_len; + + zb_get_extended_pan_id(extended_pan_id); + addr_len = ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), extended_pan_id); + + for (int i = 0; i < addr_len; ++i) { + if (ieee_addr_buf[i] != '0') { + on_join_(); + break; + } + } + } + break; + } + + /* All callbacks should either reuse or free passed buffers. + * If bufid == 0, the buffer is invalid (not passed). + */ + if (bufid) { + zb_buf_free(bufid); + } +} + +void ZigbeeComponent::zcl_device_cb(zb_bufid_t bufid) { + zb_zcl_device_callback_param_t *p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t); + zb_zcl_device_callback_id_t device_cb_id = p_device_cb_param->device_cb_id; + zb_uint16_t cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id; + zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id; + auto endpoint = p_device_cb_param->endpoint; + + ESP_LOGI(TAG, "Zcl_device_cb %s id %hd, cluster_id %d, attr_id %d, endpoint: %d", __func__, device_cb_id, cluster_id, + attr_id, endpoint); + + // endpoints are enumerated from 1 + if (global_zigbee->callbacks_.size() >= endpoint) { + global_zigbee->callbacks_[endpoint - 1](bufid); + return; + } + p_device_cb_param->status = RET_ERROR; +} + +void ZigbeeComponent::on_join_() { + this->defer([this]() { + ESP_LOGD(TAG, "Joined the network"); + this->join_trigger_.trigger(); + this->join_cb_.call(); + }); +} + +#ifdef USE_ZIGBEE_WIPE_ON_BOOT +void ZigbeeComponent::erase_flash_(int area) { + const struct flash_area *fap; + flash_area_open(area, &fap); + flash_area_erase(fap, 0, fap->fa_size); + flash_area_close(fap); +} +#endif + +void ZigbeeComponent::setup() { + global_zigbee = this; + auto err = settings_subsys_init(); + if (err) { + ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err); + return; + } + +#ifdef USE_ZIGBEE_WIPE_ON_BOOT + erase_flash_(FIXED_PARTITION_ID(ZBOSS_NVRAM)); + erase_flash_(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG)); + erase_flash_(FIXED_PARTITION_ID(SETTINGS_STORAGE)); +#endif + + ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb); + err = settings_load(); + if (err) { + ESP_LOGE(TAG, "Cannot load settings, err: %d", err); + return; + } + zigbee_enable(); +} + +void ZigbeeComponent::dump_config() { + bool wipe = false; +#ifdef USE_ZIGBEE_WIPE_ON_BOOT + wipe = true; +#endif + ESP_LOGCONFIG(TAG, + "Zigbee\n" + " Wipe on boot: %s", + YESNO(wipe)); +} + +static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) { + ESP_LOGD(TAG, "Force zboss scheduler to wake and send attribute report"); + zb_buf_free(bufid); +} + +void ZigbeeComponent::flush() { this->need_flush_ = true; } + +void ZigbeeComponent::loop() { + if (this->need_flush_) { + this->need_flush_ = false; + zb_buf_get_out_delayed_ext(send_attribute_report, 0, 0); + } +} + +void ZigbeeComponent::factory_reset() { + ESP_LOGD(TAG, "Factory reset"); + ZB_SCHEDULE_APP_CALLBACK(zb_bdb_reset_via_local_action, 0); +} + +} // namespace esphome::zigbee + +extern "C" void zboss_signal_handler(zb_uint8_t param) { + esphome::zigbee::global_zigbee->zboss_signal_handler_esphome(param); +} +#endif diff --git a/esphome/components/zigbee/zigbee_zephyr.h b/esphome/components/zigbee/zigbee_zephyr.h new file mode 100644 index 0000000000..853c6deb4d --- /dev/null +++ b/esphome/components/zigbee/zigbee_zephyr.h @@ -0,0 +1,104 @@ +#pragma once +#include "esphome/core/defines.h" +#if defined(USE_ZIGBEE) && defined(USE_NRF52) +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +extern "C" { +#include +#include +} + +// copy of ZB_DECLARE_SIMPLE_DESC. Due to https://github.com/nrfconnect/sdk-nrfxlib/pull/666 +#define ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clusters_count, out_clusters_count) \ + typedef ZB_PACKED_PRE struct zb_af_simple_desc_##ep_name##_##in_clusters_count##_##out_clusters_count##_s { \ + zb_uint8_t endpoint; /* Endpoint */ \ + zb_uint16_t app_profile_id; /* Application profile identifier */ \ + zb_uint16_t app_device_id; /* Application device identifier */ \ + zb_bitfield_t app_device_version : 4; /* Application device version */ \ + zb_bitfield_t reserved : 4; /* Reserved */ \ + zb_uint8_t app_input_cluster_count; /* Application input cluster count */ \ + zb_uint8_t app_output_cluster_count; /* Application output cluster count */ \ + /* Application input and output cluster list */ \ + zb_uint16_t app_cluster_list[(in_clusters_count) + (out_clusters_count)]; \ + } ZB_PACKED_STRUCT zb_af_simple_desc_##ep_name##_##in_clusters_count##_##out_clusters_count##_t + +#define ESPHOME_CAT7(a, b, c, d, e, f, g) a##b##c##d##e##f##g +// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC +#define ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_num, out_num) \ + ESPHOME_CAT7(zb_af_simple_desc_, ep_name, _, in_num, _, out_num, _t) + +// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC +#define ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num, ...) \ + ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clust_num, out_clust_num); \ + ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_clust_num, out_clust_num) \ + simple_desc_##ep_name = {ep_id, ZB_AF_HA_PROFILE_ID, ZB_HA_SIMPLE_SENSOR_DEVICE_ID, 0, 0, in_clust_num, \ + out_clust_num, {__VA_ARGS__}} + +// needed to use ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC +#define ESPHOME_ZB_HA_DECLARE_EP(ep_name, ep_id, cluster_list, in_cluster_num, out_cluster_num, report_attr_count, \ + ...) \ + ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_cluster_num, out_cluster_num, __VA_ARGS__); \ + ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, report_attr_count); \ + ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \ + ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \ + (zb_af_simple_desc_1_1_t *) &simple_desc_##ep_name, report_attr_count, \ + reporting_info##ep_name, 0, NULL) + +namespace esphome::zigbee { + +struct BinaryAttrs { + zb_bool_t out_of_service; + zb_bool_t present_value; + zb_uint8_t status_flags; + zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE]; +}; + +struct AnalogAttrs { + zb_bool_t out_of_service; + float present_value; + zb_uint8_t status_flags; + zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE]; + float max_present_value; + float min_present_value; + float resolution; +}; + +class ZigbeeComponent : public Component { + public: + void setup() override; + void dump_config() override; + void add_callback(zb_uint8_t endpoint, std::function &&cb) { + // endpoints are enumerated from 1 + this->callbacks_[endpoint - 1] = std::move(cb); + } + void add_join_callback(std::function &&cb) { this->join_cb_.add(std::move(cb)); } + void zboss_signal_handler_esphome(zb_bufid_t bufid); + void factory_reset(); + Trigger<> *get_join_trigger() { return &this->join_trigger_; }; + void flush(); + void loop() override; + + protected: + static void zcl_device_cb(zb_bufid_t bufid); + void on_join_(); +#ifdef USE_ZIGBEE_WIPE_ON_BOOT + void erase_flash_(int area); +#endif + StaticVector, ZIGBEE_ENDPOINTS_COUNT> callbacks_; + CallbackManager join_cb_; + Trigger<> join_trigger_; + bool need_flush_{false}; +}; + +class ZigbeeEntity { + public: + void set_parent(ZigbeeComponent *parent) { this->parent_ = parent; } + void set_end_point(zb_uint8_t end_point) { this->end_point_ = end_point; } + + protected: + zb_uint8_t end_point_{0}; + ZigbeeComponent *parent_{nullptr}; +}; + +} // namespace esphome::zigbee +#endif diff --git a/esphome/components/zigbee/zigbee_zephyr.py b/esphome/components/zigbee/zigbee_zephyr.py new file mode 100644 index 0000000000..ce55675c41 --- /dev/null +++ b/esphome/components/zigbee/zigbee_zephyr.py @@ -0,0 +1,265 @@ +from datetime import datetime + +from esphome import automation +import esphome.codegen as cg +from esphome.components.zephyr import zephyr_add_prj_conf +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_NAME, __version__ +from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.cpp_generator import ( + AssignmentExpression, + MockObj, + VariableDeclarationExpression, +) +from esphome.types import ConfigType + +from .const_zephyr import ( + CONF_ON_JOIN, + CONF_WIPE_ON_BOOT, + CONF_ZIGBEE_BINARY_SENSOR, + CONF_ZIGBEE_ID, + KEY_EP_NUMBER, + KEY_ZIGBEE, + ZB_ZCL_BASIC_ATTRS_EXT_T, + ZB_ZCL_CLUSTER_ID_BASIC, + ZB_ZCL_CLUSTER_ID_BINARY_INPUT, + ZB_ZCL_CLUSTER_ID_IDENTIFY, + ZB_ZCL_IDENTIFY_ATTRS_T, + BinaryAttrs, + ZigbeeComponent, + zigbee_ns, +) + +ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component) + +zephyr_binary_sensor = cv.Schema( + { + cv.OnlyWith(CONF_ZIGBEE_ID, ["nrf52", "zigbee"]): cv.use_id(ZigbeeComponent), + cv.OnlyWith(CONF_ZIGBEE_BINARY_SENSOR, ["nrf52", "zigbee"]): cv.declare_id( + ZigbeeBinarySensor + ), + } +) + + +async def zephyr_to_code(config: ConfigType) -> None: + zephyr_add_prj_conf("ZIGBEE", True) + zephyr_add_prj_conf("ZIGBEE_APP_UTILS", True) + zephyr_add_prj_conf("ZIGBEE_ROLE_END_DEVICE", True) + + zephyr_add_prj_conf("ZIGBEE_CHANNEL_SELECTION_MODE_MULTI", True) + + zephyr_add_prj_conf("CRYPTO", True) + + zephyr_add_prj_conf("NET_IPV6", False) + zephyr_add_prj_conf("NET_IP_ADDR_CHECK", False) + zephyr_add_prj_conf("NET_UDP", False) + + if config[CONF_WIPE_ON_BOOT]: + cg.add_define("USE_ZIGBEE_WIPE_ON_BOOT") + var = cg.new_Pvariable(config[CONF_ID]) + + if on_join_config := config.get(CONF_ON_JOIN): + await automation.build_automation(var.get_join_trigger(), [], on_join_config) + + await cg.register_component(var, config) + + await _attr_to_code(config) + CORE.add_job(_ctx_to_code, config) + + +async def _attr_to_code(config: ConfigType) -> None: + # Create the basic attributes structure and attribute list + basic_attrs = zigbee_new_variable("zigbee_basic_attrs", ZB_ZCL_BASIC_ATTRS_EXT_T) + zigbee_new_attr_list( + "zigbee_basic_attrib_list", + "ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT", + zigbee_assign(basic_attrs.zcl_version, cg.RawExpression("ZB_ZCL_VERSION")), + zigbee_assign(basic_attrs.app_version, 0), + zigbee_assign(basic_attrs.stack_version, 0), + zigbee_assign(basic_attrs.hw_version, 0), + zigbee_set_string(basic_attrs.mf_name, "esphome"), + zigbee_set_string(basic_attrs.model_id, CORE.name), + zigbee_set_string( + basic_attrs.date_code, datetime.now().strftime("%d/%m/%y %H:%M") + ), + zigbee_assign( + basic_attrs.power_source, + cg.RawExpression("ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE"), + ), + zigbee_set_string(basic_attrs.location_id, ""), + zigbee_assign( + basic_attrs.ph_env, cg.RawExpression("ZB_ZCL_BASIC_ENV_UNSPECIFIED") + ), + zigbee_set_string(basic_attrs.sw_ver, __version__), + ) + + # Create the identify attributes structure and attribute list + identify_attrs = zigbee_new_variable( + "zigbee_identify_attrs", ZB_ZCL_IDENTIFY_ATTRS_T + ) + zigbee_new_attr_list( + "zigbee_identify_attrib_list", + "ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST", + zigbee_assign( + identify_attrs.identify_time, + cg.RawExpression("ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE"), + ), + ) + + +def zigbee_new_variable(name: str, type_: str) -> cg.MockObj: + """Create a global variable with the given name and type.""" + decl = VariableDeclarationExpression(type_, "", name) + CORE.add_global(decl) + return MockObj(name, ".") + + +def zigbee_assign(target: cg.MockObj, expression: cg.RawExpression | int) -> str: + """Assign an expression to a target and return a reference to it.""" + cg.add(AssignmentExpression("", "", target, expression)) + return f"&{target}" + + +def zigbee_set_string(target: cg.MockObj, value: str) -> str: + """Set a ZCL string value and return the target name (arrays decay to pointers).""" + cg.add( + cg.RawExpression( + f"ZB_ZCL_SET_STRING_VAL({target}, {cg.safe_exp(value)}, ZB_ZCL_STRING_CONST_SIZE({cg.safe_exp(value)}))" + ) + ) + return str(target) + + +def zigbee_new_attr_list(name: str, macro: str, *args: str) -> str: + """Create an attribute list using a ZBOSS macro and return the name.""" + obj = cg.RawExpression(f"{macro}({name}, {', '.join(args)})") + CORE.add_global(obj) + return name + + +class ZigbeeClusterDesc: + """Represents a Zigbee cluster descriptor for code generation.""" + + def __init__(self, cluster_id: str, attr_list_name: str | None = None) -> None: + self._cluster_id = cluster_id + self._attr_list_name = attr_list_name + + @property + def cluster_id(self) -> str: + return self._cluster_id + + @property + def has_attrs(self) -> bool: + return self._attr_list_name is not None + + def __str__(self) -> str: + role = ( + "ZB_ZCL_CLUSTER_SERVER_ROLE" + if self._attr_list_name + else "ZB_ZCL_CLUSTER_CLIENT_ROLE" + ) + if self._attr_list_name: + attr_count = f"ZB_ZCL_ARRAY_SIZE({self._attr_list_name}, zb_zcl_attr_t)" + return f"ZB_ZCL_CLUSTER_DESC({self._cluster_id}, {attr_count}, {self._attr_list_name}, {role}, ZB_ZCL_MANUF_CODE_INVALID)" + return f"ZB_ZCL_CLUSTER_DESC({self._cluster_id}, 0, NULL, {role}, ZB_ZCL_MANUF_CODE_INVALID)" + + +def zigbee_new_cluster_list( + name: str, clusters: list[ZigbeeClusterDesc] +) -> tuple[str, list[ZigbeeClusterDesc]]: + """Create a cluster list array and return its name and the clusters.""" + # Always include basic and identify clusters first + all_clusters = [ + ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BASIC, "zigbee_basic_attrib_list"), + ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_IDENTIFY, "zigbee_identify_attrib_list"), + ] + all_clusters.extend(clusters) + + cluster_strs = [str(c) for c in all_clusters] + CORE.add_global( + cg.RawExpression( + f"zb_zcl_cluster_desc_t {name}[] = {{{', '.join(cluster_strs)}}}" + ) + ) + return (name, all_clusters) + + +def zigbee_register_ep( + ep_name: str, + cluster_list_name: str, + report_attr_count: int, + clusters: list[ZigbeeClusterDesc], + slot_index: int, +) -> None: + """Register a Zigbee endpoint.""" + in_cluster_num = sum(1 for c in clusters if c.has_attrs) + out_cluster_num = len(clusters) - in_cluster_num + cluster_ids = [c.cluster_id for c in clusters] + + # Store endpoint name for device context generation + CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER][slot_index] = ep_name + + # Generate the endpoint declaration + ep_id = slot_index + 1 # Endpoints are 1-indexed + obj = cg.RawExpression( + f"ESPHOME_ZB_HA_DECLARE_EP({ep_name}, {ep_id}, {cluster_list_name}, " + f"{in_cluster_num}, {out_cluster_num}, {report_attr_count}, {', '.join(cluster_ids)})" + ) + CORE.add_global(obj) + + +@coroutine_with_priority(CoroPriority.LATE) +async def _ctx_to_code(config: ConfigType) -> None: + cg.add_define("ZIGBEE_ENDPOINTS_COUNT", len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])) + cg.add_global( + cg.RawExpression( + f"ZBOSS_DECLARE_DEVICE_CTX_EP_VA(zb_device_ctx, &{', &'.join(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])})" + ) + ) + cg.add(cg.RawExpression("ZB_AF_REGISTER_DEVICE_CTX(&zb_device_ctx)")) + + +async def zephyr_setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None: + CORE.add_job(_add_binary_sensor, entity, config) + + +async def _add_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None: + # Find the next available endpoint slot + slot_index = next( + (i for i, v in enumerate(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]) if v == ""), None + ) + + # Create unique names for this sensor's variables based on slot index + prefix = f"zigbee_ep{slot_index + 1}" + attrs_name = f"{prefix}_binary_attrs" + attr_list_name = f"{prefix}_binary_input_attrib_list" + cluster_list_name = f"{prefix}_cluster_list" + ep_name = f"{prefix}_ep" + + # Create the binary attributes structure + binary_attrs = zigbee_new_variable(attrs_name, BinaryAttrs) + attr_list = zigbee_new_attr_list( + attr_list_name, + "ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST", + zigbee_assign(binary_attrs.out_of_service, 0), + zigbee_assign(binary_attrs.present_value, 0), + zigbee_assign(binary_attrs.status_flags, 0), + zigbee_set_string(binary_attrs.description, config[CONF_NAME]), + ) + + # Create cluster list and register endpoint + cluster_list_name, clusters = zigbee_new_cluster_list( + cluster_list_name, + [ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BINARY_INPUT, attr_list)], + ) + zigbee_register_ep(ep_name, cluster_list_name, 2, clusters, slot_index) + + # Create the ZigbeeBinarySensor component + var = cg.new_Pvariable(config[CONF_ZIGBEE_BINARY_SENSOR], entity) + await cg.register_component(var, config) + + cg.add(var.set_end_point(slot_index + 1)) + cg.add(var.set_cluster_attributes(binary_attrs)) + hub = await cg.get_variable(config[CONF_ZIGBEE_ID]) + cg.add(var.set_parent(hub)) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1fddc426d4..cee46a2df0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -299,6 +299,9 @@ #define USE_NRF52_UICR_ERASE #define USE_SOFTDEVICE_ID 7 #define USE_SOFTDEVICE_VERSION 1 +#define USE_ZIGBEE +#define USE_ZIGBEE_WIPE_ON_BOOT +#define ZIGBEE_ENDPOINTS_COUNT 8 #endif // Disabled feature flags diff --git a/script/helpers_zephyr.py b/script/helpers_zephyr.py index f72b335e64..1242a60cf4 100644 --- a/script/helpers_zephyr.py +++ b/script/helpers_zephyr.py @@ -17,6 +17,7 @@ def load_idedata(environment, temp_folder, platformio_ini): """ #include int main() { return 0;} +extern "C" void zboss_signal_handler() {}; """, encoding="utf-8", ) @@ -27,6 +28,12 @@ int main() { return 0;} CONFIG_NEWLIB_LIBC=y CONFIG_BT=y CONFIG_ADC=y +#zigbee begin +CONFIG_ZIGBEE=y +CONFIG_CRYPTO=y +CONFIG_NVS=y +CONFIG_SETTINGS=y +#zigbee end """, encoding="utf-8", ) @@ -44,10 +51,11 @@ CONFIG_ADC=y def extract_defines(command): define_pattern = re.compile(r"-D\s*([^\s]+)") + ignore_prefixes = ("_ASMLANGUAGE", "NRF_802154_ECB_PRIORITY=") return [ - match + match.replace("\\", "") for match in define_pattern.findall(command) - if match not in ("_ASMLANGUAGE") + if not any(match.startswith(prefix) for prefix in ignore_prefixes) ] def find_cxx_path(commands): diff --git a/tests/components/zigbee/common.yaml b/tests/components/zigbee/common.yaml new file mode 100644 index 0000000000..eb30205446 --- /dev/null +++ b/tests/components/zigbee/common.yaml @@ -0,0 +1,34 @@ +--- +binary_sensor: + - platform: template + name: "Garage Door Open 1" + - platform: template + name: "Garage Door Open 2" + - platform: template + name: "Garage Door Open 3" + - platform: template + name: "Garage Door Open 4" + - platform: template + name: "Garage Door Open 5" + - platform: template + name: "Garage Door Open 6" + - platform: template + name: "Garage Door Open 7" + internal: True + - platform: template + name: "Garage Door Open 8" + - platform: template + name: "Garage Door Open 9" + +zigbee: + wipe_on_boot: true + on_join: + then: + - logger.log: "Joined network" + +output: + - platform: template + id: output_factory + type: binary + write_action: + - zigbee.factory_reset diff --git a/tests/components/zigbee/test.nrf52-adafruit.yaml b/tests/components/zigbee/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/zigbee/test.nrf52-adafruit.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/zigbee/test.nrf52-mcumgr.yaml b/tests/components/zigbee/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/zigbee/test.nrf52-mcumgr.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/zigbee/test.nrf52-xiao-ble.yaml b/tests/components/zigbee/test.nrf52-xiao-ble.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/zigbee/test.nrf52-xiao-ble.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From a011d5ea9628cf955111d34a70353c2aa40a963f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:14:57 -1000 Subject: [PATCH 12/30] [sht3xd] Combine log statements to reduce loop blocking (#12957) --- esphome/components/sht3xd/sht3xd.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index 79f1674020..d473df43c7 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -60,8 +60,10 @@ void SHT3XDComponent::dump_config() { ESP_LOGE(TAG, " Communication with SHT3xD failed!"); return; } - ESP_LOGD(TAG, " Serial Number: 0x%08" PRIX32, this->serial_number_); - ESP_LOGD(TAG, " Heater Enabled: %s", this->heater_enabled_ ? "true" : "false"); + ESP_LOGD(TAG, + " Serial Number: 0x%08" PRIX32 "\n" + " Heater Enabled: %s", + this->serial_number_, TRUEFALSE(this->heater_enabled_)); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); From 2295f57dec2dc5fd31851f7faff0bb976d9d496c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:51:11 -1000 Subject: [PATCH 13/30] [st7567_i2c] Combine log statements to reduce loop blocking (#12975) --- esphome/components/st7567_i2c/st7567_i2c.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/st7567_i2c/st7567_i2c.cpp b/esphome/components/st7567_i2c/st7567_i2c.cpp index 14c21d5148..3214339571 100644 --- a/esphome/components/st7567_i2c/st7567_i2c.cpp +++ b/esphome/components/st7567_i2c/st7567_i2c.cpp @@ -20,14 +20,14 @@ void I2CST7567::setup() { void I2CST7567::dump_config() { LOG_DISPLAY("", "I2CST7567", this); - LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); - LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, + " Model: %s\n" " Mirror X: %s\n" " Mirror Y: %s\n" " Invert Colors: %s", - YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_)); + this->model_str_(), YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_)); + LOG_I2C_DEVICE(this); + LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { From 405b26426c4a7334c130ecf18c2c3b5e640bf139 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:51:24 -1000 Subject: [PATCH 14/30] [st7567_spi] Combine log statements to reduce loop blocking (#12976) --- esphome/components/st7567_spi/st7567_spi.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/st7567_spi/st7567_spi.cpp b/esphome/components/st7567_spi/st7567_spi.cpp index 813afcf682..7476fd7c8d 100644 --- a/esphome/components/st7567_spi/st7567_spi.cpp +++ b/esphome/components/st7567_spi/st7567_spi.cpp @@ -18,13 +18,15 @@ void SPIST7567::setup() { void SPIST7567::dump_config() { LOG_DISPLAY("", "SPI ST7567", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Invert Colors: %s", + this->model_str_(), YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->invert_colors_)); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->mirror_x_)); - ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->mirror_y_)); - ESP_LOGCONFIG(TAG, " Invert Colors: %s", YESNO(this->invert_colors_)); LOG_UPDATE_INTERVAL(this); } From 1d0f36ba35b7d0fc21e0bc2306580e915c2cbadc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:51:37 -1000 Subject: [PATCH 15/30] [st7789v] Combine log statements to reduce loop blocking (#12978) --- esphome/components/st7789v/st7789v.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index ade9c1126f..cd0b6cabc3 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -127,15 +127,15 @@ void ST7789V::dump_config() { " Width: %u\n" " Height Offset: %u\n" " Width Offset: %u\n" - " 8-bit color mode: %s", + " 8-bit color mode: %s\n" + " Data rate: %dMHz", this->model_str_, this->height_, this->width_, this->offset_height_, this->offset_width_, - YESNO(this->eightbitcolor_)); + YESNO(this->eightbitcolor_), (unsigned) (this->data_rate_ / 1000000)); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" B/L Pin: ", this->backlight_pin_); LOG_UPDATE_INTERVAL(this); - ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); #ifdef USE_POWER_SUPPLY ESP_LOGCONFIG(TAG, " Power Supply Configured: yes"); #endif From 4bc1a02fc2a5e9f2a0e8dde600a552fb6a0ecfb5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:52:03 -1000 Subject: [PATCH 16/30] [shtcx] Combine log statements to reduce loop blocking (#12960) --- esphome/components/shtcx/shtcx.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index d532bd7f44..933dd9bde9 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -49,8 +49,10 @@ void SHTCXComponent::setup() { } void SHTCXComponent::dump_config() { - ESP_LOGCONFIG(TAG, "SHTCx:"); - ESP_LOGCONFIG(TAG, " Model: %s (%04x)", to_string(this->type_), this->sensor_id_); + ESP_LOGCONFIG(TAG, + "SHTCx:\n" + " Model: %s (%04x)", + to_string(this->type_), this->sensor_id_); LOG_I2C_DEVICE(this); if (this->is_failed()) { ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); From c742db48b8857d684f6e291271a39cb8069f34f0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:52:57 -1000 Subject: [PATCH 17/30] [sim800l] Combine log statements to reduce loop blocking (#12961) --- esphome/components/sim800l/sim800l.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 55cadcf182..e3edda0e72 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -323,8 +323,10 @@ void Sim800LComponent::parse_cmd_(std::string message) { kick ESPHome callback now */ if (ok || message.compare(0, 6, "+CMGL:") == 0) { - ESP_LOGD(TAG, "Received SMS from: %s", this->sender_.c_str()); - ESP_LOGD(TAG, "%s", this->message_.c_str()); + ESP_LOGD(TAG, + "Received SMS from: %s\n" + "%s", + this->sender_.c_str(), this->message_.c_str()); this->sms_received_callback_.call(this->message_, this->sender_); this->state_ = STATE_RECEIVED_SMS; } else { From 9128fc312076812f0a8f0b0b04f96956f4829441 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:54:03 -1000 Subject: [PATCH 18/30] [sm16716] Combine log statements to reduce loop blocking (#12962) --- esphome/components/sm16716/sm16716.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/sm16716/sm16716.cpp b/esphome/components/sm16716/sm16716.cpp index aa33b7b679..b8e293929b 100644 --- a/esphome/components/sm16716/sm16716.cpp +++ b/esphome/components/sm16716/sm16716.cpp @@ -14,11 +14,13 @@ void SM16716::setup() { this->pwm_amounts_.resize(this->num_channels_, 0); } void SM16716::dump_config() { - ESP_LOGCONFIG(TAG, "SM16716:"); + ESP_LOGCONFIG(TAG, + "SM16716:\n" + " Total number of channels: %u\n" + " Number of chips: %u", + this->num_channels_, this->num_chips_); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " Total number of channels: %u", this->num_channels_); - ESP_LOGCONFIG(TAG, " Number of chips: %u", this->num_chips_); } void SM16716::loop() { if (!this->update_) From 47223965b6924afae80c40c0084a4ee2c9228245 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:54:17 -1000 Subject: [PATCH 19/30] [sm2135] Combine log statements to reduce loop blocking (#12963) --- esphome/components/sm2135/sm2135.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/sm2135/sm2135.cpp b/esphome/components/sm2135/sm2135.cpp index e55f836929..1293c3f321 100644 --- a/esphome/components/sm2135/sm2135.cpp +++ b/esphome/components/sm2135/sm2135.cpp @@ -34,11 +34,13 @@ void SM2135::setup() { } void SM2135::dump_config() { - ESP_LOGCONFIG(TAG, "SM2135:"); + ESP_LOGCONFIG(TAG, + "SM2135:\n" + " CW Current: %dmA\n" + " RGB Current: %dmA", + 10 + (this->cw_current_ * 5), 10 + (this->rgb_current_ * 5)); LOG_PIN(" Data Pin: ", this->data_pin_); LOG_PIN(" Clock Pin: ", this->clock_pin_); - ESP_LOGCONFIG(TAG, " CW Current: %dmA", 10 + (this->cw_current_ * 5)); - ESP_LOGCONFIG(TAG, " RGB Current: %dmA", 10 + (this->rgb_current_ * 5)); } void SM2135::write_byte_(uint8_t data) { From f67a8d0d1fa9f5d10149f1be76f417d21cb45c68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:55:11 -1000 Subject: [PATCH 20/30] [sonoff_d1] Combine log statements to reduce loop blocking (#12966) --- esphome/components/sonoff_d1/sonoff_d1.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/esphome/components/sonoff_d1/sonoff_d1.cpp b/esphome/components/sonoff_d1/sonoff_d1.cpp index 0ecde83b8b..7b99086546 100644 --- a/esphome/components/sonoff_d1/sonoff_d1.cpp +++ b/esphome/components/sonoff_d1/sonoff_d1.cpp @@ -93,8 +93,10 @@ bool SonoffD1Output::read_command_(uint8_t *cmd, size_t &len) { if (this->read_array(cmd, 6)) { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE char hex_buf[format_hex_pretty_size(6)]; - ESP_LOGV(TAG, "[%04d] Reading from dimmer:", this->write_count_); - ESP_LOGV(TAG, "[%04d] %s", this->write_count_, format_hex_pretty_to(hex_buf, cmd, 6)); + ESP_LOGV(TAG, + "[%04d] Reading from dimmer:\n" + "[%04d] %s", + this->write_count_, this->write_count_, format_hex_pretty_to(hex_buf, cmd, 6)); #endif if (cmd[0] != 0xAA || cmd[1] != 0x55) { @@ -188,8 +190,10 @@ bool SonoffD1Output::write_command_(uint8_t *cmd, const size_t len, bool needs_a do { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE char hex_buf[format_hex_pretty_size(SONOFF_D1_MAX_CMD_SIZE)]; - ESP_LOGV(TAG, "[%04d] Writing to the dimmer:", this->write_count_); - ESP_LOGV(TAG, "[%04d] %s", this->write_count_, format_hex_pretty_to(hex_buf, cmd, len)); + ESP_LOGV(TAG, + "[%04d] Writing to the dimmer:\n" + "[%04d] %s", + this->write_count_, this->write_count_, format_hex_pretty_to(hex_buf, cmd, len)); #endif this->write_array(cmd, len); this->write_count_++; From 9cd003034c1324b631d562deb9ef0d82153df0c7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:55:31 -1000 Subject: [PATCH 21/30] [spi_device] Combine log statements to reduce loop blocking (#12967) --- esphome/components/spi_device/spi_device.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/spi_device/spi_device.cpp b/esphome/components/spi_device/spi_device.cpp index dbfbc9eccb..4cc7286ba9 100644 --- a/esphome/components/spi_device/spi_device.cpp +++ b/esphome/components/spi_device/spi_device.cpp @@ -11,9 +11,11 @@ static const char *const TAG = "spi_device"; void SPIDeviceComponent::setup() { this->spi_setup(); } void SPIDeviceComponent::dump_config() { - ESP_LOGCONFIG(TAG, "SPIDevice"); + ESP_LOGCONFIG(TAG, + "SPIDevice\n" + " Mode: %d", + this->mode_); LOG_PIN(" CS pin: ", this->cs_); - ESP_LOGCONFIG(TAG, " Mode: %d", this->mode_); if (this->data_rate_ < 1000000) { ESP_LOGCONFIG(TAG, " Data rate: %" PRId32 "kHz", this->data_rate_ / 1000); } else { From ae3cdeda99f9803a7916e4a27890af7222fdb74f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:55:55 -1000 Subject: [PATCH 22/30] [ssd1325_spi] Combine log statements to reduce loop blocking (#12972) --- esphome/components/ssd1325_spi/ssd1325_spi.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/ssd1325_spi/ssd1325_spi.cpp b/esphome/components/ssd1325_spi/ssd1325_spi.cpp index 3c9dfd3324..07a5119d8f 100644 --- a/esphome/components/ssd1325_spi/ssd1325_spi.cpp +++ b/esphome/components/ssd1325_spi/ssd1325_spi.cpp @@ -19,12 +19,14 @@ void SPISSD1325::setup() { } void SPISSD1325::dump_config() { LOG_DISPLAY("", "SPI SSD1325", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Initial Brightness: %.2f\n" + " External VCC: %s", + this->model_str_(), this->brightness_, YESNO(this->external_vcc_)); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_); - ESP_LOGCONFIG(TAG, " External VCC: %s", YESNO(this->external_vcc_)); LOG_UPDATE_INTERVAL(this); } void SPISSD1325::command(uint8_t value) { From d8387799d9dfb9fc1d7d09a48f990e1fa8e08d3d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:56:30 -1000 Subject: [PATCH 23/30] [sm2335] Combine log statements to reduce loop blocking (#12965) --- esphome/components/sm2335/sm2335.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/sm2335/sm2335.cpp b/esphome/components/sm2335/sm2335.cpp index 0580a782f5..f860517021 100644 --- a/esphome/components/sm2335/sm2335.cpp +++ b/esphome/components/sm2335/sm2335.cpp @@ -15,13 +15,13 @@ void SM2335::setup() { } void SM2335::dump_config() { - ESP_LOGCONFIG(TAG, "sm2335:"); - LOG_PIN(" Data Pin: ", this->data_pin_); - LOG_PIN(" Clock Pin: ", this->clock_pin_); ESP_LOGCONFIG(TAG, + "sm2335:\n" " Color Channels Max Power: %u\n" " White Channels Max Power: %u", this->max_power_color_channels_, this->max_power_white_channels_); + LOG_PIN(" Data Pin: ", this->data_pin_); + LOG_PIN(" Clock Pin: ", this->clock_pin_); } } // namespace sm2335 From a2bb9468ff3fa6c60498884d58b7c98c09e624c4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:57:43 -1000 Subject: [PATCH 24/30] [sm2235] Combine log statements to reduce loop blocking (#12964) Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/sm2235/sm2235.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/sm2235/sm2235.cpp b/esphome/components/sm2235/sm2235.cpp index 820fcb521a..4476862318 100644 --- a/esphome/components/sm2235/sm2235.cpp +++ b/esphome/components/sm2235/sm2235.cpp @@ -15,13 +15,13 @@ void SM2235::setup() { } void SM2235::dump_config() { - ESP_LOGCONFIG(TAG, "sm2235:"); - LOG_PIN(" Data Pin: ", this->data_pin_); - LOG_PIN(" Clock Pin: ", this->clock_pin_); ESP_LOGCONFIG(TAG, + "SM2235:\n" " Color Channels Max Power: %u\n" " White Channels Max Power: %u", this->max_power_color_channels_, this->max_power_white_channels_); + LOG_PIN(" Data Pin: ", this->data_pin_); + LOG_PIN(" Clock Pin: ", this->clock_pin_); } } // namespace sm2235 From ed332a034b87e09fb041965cec0f359edab6cd3b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:59:36 -1000 Subject: [PATCH 25/30] [ssd1351_spi] Combine log statements to reduce loop blocking (#12974) --- esphome/components/ssd1351_spi/ssd1351_spi.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ssd1351_spi/ssd1351_spi.cpp b/esphome/components/ssd1351_spi/ssd1351_spi.cpp index ffac07b82b..b046f0adcb 100644 --- a/esphome/components/ssd1351_spi/ssd1351_spi.cpp +++ b/esphome/components/ssd1351_spi/ssd1351_spi.cpp @@ -19,11 +19,13 @@ void SPISSD1351::setup() { } void SPISSD1351::dump_config() { LOG_DISPLAY("", "SPI SSD1351", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Initial Brightness: %.2f", + this->model_str_(), this->brightness_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_); LOG_UPDATE_INTERVAL(this); } void SPISSD1351::command(uint8_t value) { From 06101c54a5ff2b72efd3dbd1045ff080b83f1bc6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 16:59:52 -1000 Subject: [PATCH 26/30] [ssd1327_spi] Combine log statements to reduce loop blocking (#12973) --- esphome/components/ssd1327_spi/ssd1327_spi.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ssd1327_spi/ssd1327_spi.cpp b/esphome/components/ssd1327_spi/ssd1327_spi.cpp index c26238ae19..54d1a51100 100644 --- a/esphome/components/ssd1327_spi/ssd1327_spi.cpp +++ b/esphome/components/ssd1327_spi/ssd1327_spi.cpp @@ -19,11 +19,13 @@ void SPISSD1327::setup() { } void SPISSD1327::dump_config() { LOG_DISPLAY("", "SPI SSD1327", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Initial Brightness: %.2f", + this->model_str_(), this->brightness_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_); LOG_UPDATE_INTERVAL(this); } void SPISSD1327::command(uint8_t value) { From 2381ea7ff5a62b41e416fbdb8dc363bdcb3dc17a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 17:00:09 -1000 Subject: [PATCH 27/30] [ssd1322_spi] Combine log statements to reduce loop blocking (#12971) --- esphome/components/ssd1322_spi/ssd1322_spi.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ssd1322_spi/ssd1322_spi.cpp b/esphome/components/ssd1322_spi/ssd1322_spi.cpp index 6a8918353b..bc7d298922 100644 --- a/esphome/components/ssd1322_spi/ssd1322_spi.cpp +++ b/esphome/components/ssd1322_spi/ssd1322_spi.cpp @@ -19,11 +19,13 @@ void SPISSD1322::setup() { } void SPISSD1322::dump_config() { LOG_DISPLAY("", "SPI SSD1322", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + ESP_LOGCONFIG(TAG, + " Model: %s\n" + " Initial Brightness: %.2f", + this->model_str_(), this->brightness_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGCONFIG(TAG, " Initial Brightness: %.2f", this->brightness_); LOG_UPDATE_INTERVAL(this); } void SPISSD1322::command(uint8_t value) { From 0bd8a7e1a02c8d1960b77031f2a3c1434daf83a6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 17:00:21 -1000 Subject: [PATCH 28/30] [ssd1306_spi] Combine log statements to reduce loop blocking (#12970) --- esphome/components/ssd1306_spi/ssd1306_spi.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index d93742c0e5..db28dfc564 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -16,19 +16,19 @@ void SPISSD1306::setup() { } void SPISSD1306::dump_config() { LOG_DISPLAY("", "SPI SSD1306", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); - LOG_PIN(" CS Pin: ", this->cs_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, + " Model: %s\n" " External VCC: %s\n" " Flip X: %s\n" " Flip Y: %s\n" " Offset X: %d\n" " Offset Y: %d\n" " Inverted Color: %s", - YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, - this->offset_y_, YESNO(this->invert_)); + this->model_str_(), YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), + this->offset_x_, this->offset_y_, YESNO(this->invert_)); + LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_UPDATE_INTERVAL(this); } void SPISSD1306::command(uint8_t value) { From 28d30fdddbdd3272ac60f7395f55552be7ac688b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 17:00:40 -1000 Subject: [PATCH 29/30] [ssd1306_i2c] Combine log statements to reduce loop blocking (#12969) --- esphome/components/ssd1306_i2c/ssd1306_i2c.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index 8e490834bc..ab6fee7b02 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -20,18 +20,18 @@ void I2CSSD1306::setup() { } void I2CSSD1306::dump_config() { LOG_DISPLAY("", "I2C SSD1306", this); - LOG_I2C_DEVICE(this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); - LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, + " Model: %s\n" " External VCC: %s\n" " Flip X: %s\n" " Flip Y: %s\n" " Offset X: %d\n" " Offset Y: %d\n" " Inverted Color: %s", - YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), this->offset_x_, - this->offset_y_, YESNO(this->invert_)); + this->model_str_(), YESNO(this->external_vcc_), YESNO(this->flip_x_), YESNO(this->flip_y_), + this->offset_x_, this->offset_y_, YESNO(this->invert_)); + LOG_I2C_DEVICE(this); + LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_UPDATE_INTERVAL(this); if (this->error_code_ == COMMUNICATION_FAILED) { From 80ab9485e00c2ea9debe20ccfc5b3290bafe4cf0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 4 Jan 2026 17:00:59 -1000 Subject: [PATCH 30/30] [spi_led_strip] Combine log statements to reduce loop blocking (#12968) --- esphome/components/spi_led_strip/spi_led_strip.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/spi_led_strip/spi_led_strip.cpp b/esphome/components/spi_led_strip/spi_led_strip.cpp index 85c10ee87d..afb51afe3a 100644 --- a/esphome/components/spi_led_strip/spi_led_strip.cpp +++ b/esphome/components/spi_led_strip/spi_led_strip.cpp @@ -34,8 +34,10 @@ light::LightTraits SpiLedStrip::get_traits() { return traits; } void SpiLedStrip::dump_config() { - esph_log_config(TAG, "SPI LED Strip:"); - esph_log_config(TAG, " LEDs: %d", this->num_leds_); + esph_log_config(TAG, + "SPI LED Strip:\n" + " LEDs: %d", + this->num_leds_); if (this->data_rate_ >= spi::DATA_RATE_1MHZ) { esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); } else {