From babaa1db3f7e6d09448d4745772493d7aab7169e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 23 Jul 2025 23:31:13 +1200 Subject: [PATCH 01/20] [i2c] Use ``i2c_master_probe`` to scan i2c bus (#9831) --- esphome/components/i2c/i2c_bus.h | 2 +- esphome/components/i2c/i2c_bus_arduino.cpp | 2 +- esphome/components/i2c/i2c_bus_esp_idf.cpp | 17 ++++++++++++++--- esphome/components/i2c/i2c_bus_esp_idf.h | 3 ++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/esphome/components/i2c/i2c_bus.h b/esphome/components/i2c/i2c_bus.h index 5c1e15d814..da94aa940d 100644 --- a/esphome/components/i2c/i2c_bus.h +++ b/esphome/components/i2c/i2c_bus.h @@ -94,7 +94,7 @@ class I2CBus { protected: /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair /// that contains the address and the corresponding bool presence flag. - void i2c_scan_() { + virtual void i2c_scan() { for (uint8_t address = 8; address < 120; address++) { auto err = writev(address, nullptr, 0); if (err == ERROR_OK) { diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index a85df0a4cd..1e84f122de 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -42,7 +42,7 @@ void ArduinoI2CBus::setup() { this->initialized_ = true; if (this->scan_) { ESP_LOGV(TAG, "Scanning bus for active devices"); - this->i2c_scan_(); + this->i2c_scan(); } } diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index c57d537bdb..141e6a670d 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -1,13 +1,13 @@ #ifdef USE_ESP_IDF #include "i2c_bus_esp_idf.h" +#include #include #include #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) #define SOC_HP_I2C_NUM SOC_I2C_NUM @@ -78,7 +78,7 @@ void IDFI2CBus::setup() { if (this->scan_) { ESP_LOGV(TAG, "Scanning for devices"); - this->i2c_scan_(); + this->i2c_scan(); } #else #if SOC_HP_I2C_NUM > 1 @@ -125,7 +125,7 @@ void IDFI2CBus::setup() { initialized_ = true; if (this->scan_) { ESP_LOGV(TAG, "Scanning bus for active devices"); - this->i2c_scan_(); + this->i2c_scan(); } #endif } @@ -167,6 +167,17 @@ void IDFI2CBus::dump_config() { } } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) +void IDFI2CBus::i2c_scan() { + for (uint8_t address = 8; address < 120; address++) { + auto err = i2c_master_probe(this->bus_, address, 20); + if (err == ESP_OK) { + this->scan_results_.emplace_back(address, true); + } + } +} +#endif + ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { // logging is only enabled with vv level, if warnings are shown the caller // should log them diff --git a/esphome/components/i2c/i2c_bus_esp_idf.h b/esphome/components/i2c/i2c_bus_esp_idf.h index 8d325de6bc..4e8f86fd0c 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.h +++ b/esphome/components/i2c/i2c_bus_esp_idf.h @@ -2,9 +2,9 @@ #ifdef USE_ESP_IDF +#include "esp_idf_version.h" #include "esphome/core/component.h" #include "i2c_bus.h" -#include "esp_idf_version.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) #include #else @@ -46,6 +46,7 @@ class IDFI2CBus : public InternalI2CBus, public Component { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) i2c_master_dev_handle_t dev_; i2c_master_bus_handle_t bus_; + void i2c_scan() override; #endif i2c_port_t port_; uint8_t sda_pin_; From 378b687a821e0af060bb7187c3e43f773496b55f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 01:31:30 -1000 Subject: [PATCH 02/20] [core] Restore COMPONENT_STATE_LOOP_DONE check in calculate_looping_components (#9832) --- esphome/core/application.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 4fedd36120..3ac17849dd 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -271,16 +271,26 @@ void Application::calculate_looping_components_() { // Pre-reserve vector to avoid reallocations this->looping_components_.reserve(total_looping); - // Add all components with loop override - // When called at start of setup, all components are in CONSTRUCTION state - // so none will be LOOP_DONE yet - they'll all go in the active section + // Add all components with loop override that aren't already LOOP_DONE + // Some components (like logger) may call disable_loop() during initialization + // before setup runs, so we need to respect their LOOP_DONE state for (auto *obj : this->components_) { - if (obj->has_overridden_loop()) { + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { this->looping_components_.push_back(obj); } } this->looping_components_active_end_ = this->looping_components_.size(); + + // Then add any components that are already LOOP_DONE to the inactive section + // This handles components that called disable_loop() during initialization + for (auto *obj : this->components_) { + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { + this->looping_components_.push_back(obj); + } + } } void Application::disable_component_loop_(Component *component) { From 6ac1073469d131807c31964a09a008a0d70a2c3c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 01:32:35 -1000 Subject: [PATCH 03/20] [ci] Support C++17 nested namespace syntax in linter (#9826) --- script/ci-custom.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script/ci-custom.py b/script/ci-custom.py index 1172c7152f..e726fcefc0 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -575,13 +575,15 @@ def lint_namespace(fname, content): expected_name = re.match( r"^esphome/components/([^/]+)/.*", fname.replace(os.path.sep, "/") ).group(1) - search = f"namespace {expected_name}" - if search in content: + # Check for both old style and C++17 nested namespace syntax + search_old = f"namespace {expected_name}" + search_new = f"namespace esphome::{expected_name}" + if search_old in content or search_new in content: return None return ( "Invalid namespace found in C++ file. All integration C++ files should put all " "functions in a separate namespace that matches the integration's name. " - f"Please make sure the file contains {highlight(search)}" + f"Please make sure the file contains {highlight(search_old)} or {highlight(search_new)}" ) From e94cb0327208749507de219dde96496f244ec781 Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Wed, 23 Jul 2025 23:36:20 +0200 Subject: [PATCH 04/20] [modem] network component change (#9801) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/network/util.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index a8e792a2d7..bf76aefc30 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -13,15 +13,27 @@ #include "esphome/components/openthread/openthread.h" #endif +#ifdef USE_MODEM +#include "esphome/components/modem/modem_component.h" +#endif + namespace esphome { namespace network { +// The order of the components is important: WiFi should come after any possible main interfaces (it may be used as +// an AP that use a previous interface for NAT). + bool is_connected() { #ifdef USE_ETHERNET if (ethernet::global_eth_component != nullptr && ethernet::global_eth_component->is_connected()) return true; #endif +#ifdef USE_MODEM + if (modem::global_modem_component != nullptr) + return modem::global_modem_component->is_connected(); +#endif + #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->is_connected(); @@ -39,6 +51,11 @@ bool is_connected() { } bool is_disabled() { +#ifdef USE_MODEM + if (modem::global_modem_component != nullptr) + return modem::global_modem_component->is_disabled(); +#endif + #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->is_disabled(); @@ -51,6 +68,12 @@ network::IPAddresses get_ip_addresses() { if (ethernet::global_eth_component != nullptr) return ethernet::global_eth_component->get_ip_addresses(); #endif + +#ifdef USE_MODEM + if (modem::global_modem_component != nullptr) + return modem::global_modem_component->get_ip_addresses(); +#endif + #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->get_ip_addresses(); @@ -67,6 +90,12 @@ std::string get_use_address() { if (ethernet::global_eth_component != nullptr) return ethernet::global_eth_component->get_use_address(); #endif + +#ifdef USE_MODEM + if (modem::global_modem_component != nullptr) + return modem::global_modem_component->get_use_address(); +#endif + #ifdef USE_WIFI if (wifi::global_wifi_component != nullptr) return wifi::global_wifi_component->get_use_address(); From 49df68beb6755a603bceda1a37c546c4b510d9ec Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 24 Jul 2025 07:52:07 +1000 Subject: [PATCH 05/20] [gt911] i2c fixes (#9822) --- .../gt911/touchscreen/gt911_touchscreen.cpp | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index 1cead70181..5c540effd0 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -8,6 +8,8 @@ namespace gt911 { static const char *const TAG = "gt911.touchscreen"; +static const uint8_t PRIMARY_ADDRESS = 0x5D; // default I2C address for GT911 +static const uint8_t SECONDARY_ADDRESS = 0x14; // secondary I2C address for GT911 static const uint8_t GET_TOUCH_STATE[2] = {0x81, 0x4E}; static const uint8_t CLEAR_TOUCH_STATE[3] = {0x81, 0x4E, 0x00}; static const uint8_t GET_TOUCHES[2] = {0x81, 0x4F}; @@ -18,8 +20,7 @@ static const size_t MAX_BUTTONS = 4; // max number of buttons scanned #define ERROR_CHECK(err) \ if ((err) != i2c::ERROR_OK) { \ - ESP_LOGE(TAG, "Failed to communicate!"); \ - this->status_set_warning(); \ + this->status_set_warning("Communication failure"); \ return; \ } @@ -30,31 +31,31 @@ void GT911Touchscreen::setup() { this->reset_pin_->setup(); this->reset_pin_->digital_write(false); if (this->interrupt_pin_ != nullptr) { - // The interrupt pin is used as an input during reset to select the I2C address. + // temporarily set the interrupt pin to output to control address selection this->interrupt_pin_->pin_mode(gpio::FLAG_OUTPUT); - this->interrupt_pin_->setup(); this->interrupt_pin_->digital_write(false); } delay(2); this->reset_pin_->digital_write(true); delay(50); // NOLINT - if (this->interrupt_pin_ != nullptr) { - this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT); - this->interrupt_pin_->setup(); - } + } + if (this->interrupt_pin_ != nullptr) { + // set pre-configured input mode + this->interrupt_pin_->setup(); } // check the configuration of the int line. uint8_t data[4]; - err = this->write(GET_SWITCHES, 2); + err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES)); + if (err != i2c::ERROR_OK && this->address_ == PRIMARY_ADDRESS) { + this->address_ = SECONDARY_ADDRESS; + err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES)); + } if (err == i2c::ERROR_OK) { err = this->read(data, 1); if (err == i2c::ERROR_OK) { - ESP_LOGD(TAG, "Read from switches: 0x%02X", data[0]); + ESP_LOGD(TAG, "Read from switches at address 0x%02X: 0x%02X", this->address_, data[0]); if (this->interrupt_pin_ != nullptr) { - // datasheet says NOT to use pullup/down on the int line. - this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT); - this->interrupt_pin_->setup(); this->attach_interrupt_(this->interrupt_pin_, (data[0] & 1) ? gpio::INTERRUPT_FALLING_EDGE : gpio::INTERRUPT_RISING_EDGE); } @@ -63,7 +64,7 @@ void GT911Touchscreen::setup() { if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) { // no calibration? Attempt to read the max values from the touchscreen. if (err == i2c::ERROR_OK) { - err = this->write(GET_MAX_VALUES, 2); + err = this->write(GET_MAX_VALUES, sizeof(GET_MAX_VALUES)); if (err == i2c::ERROR_OK) { err = this->read(data, sizeof(data)); if (err == i2c::ERROR_OK) { @@ -75,15 +76,12 @@ void GT911Touchscreen::setup() { } } if (err != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Failed to read calibration values from touchscreen!"); - this->mark_failed(); + this->mark_failed("Failed to read calibration"); return; } } if (err != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Failed to communicate!"); - this->mark_failed(); - return; + this->mark_failed("Failed to communicate"); } ESP_LOGCONFIG(TAG, "GT911 Touchscreen setup complete"); @@ -94,7 +92,7 @@ void GT911Touchscreen::update_touches() { uint8_t touch_state = 0; uint8_t data[MAX_TOUCHES + 1][8]; // 8 bytes each for each point, plus extra space for the key byte - err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE), false); + err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE)); ERROR_CHECK(err); err = this->read(&touch_state, 1); ERROR_CHECK(err); @@ -106,7 +104,7 @@ void GT911Touchscreen::update_touches() { return; } - err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES), false); + err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES)); ERROR_CHECK(err); // num_of_touches is guaranteed to be 0..5. Also read the key data err = this->read(data[0], sizeof(data[0]) * num_of_touches + 1); @@ -132,6 +130,7 @@ void GT911Touchscreen::dump_config() { ESP_LOGCONFIG(TAG, "GT911 Touchscreen:"); LOG_I2C_DEVICE(this); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); } } // namespace gt911 From 0744abe0980dd9e2492e3d57a5ef4972815ed0fe Mon Sep 17 00:00:00 2001 From: Eric Hoffmann Date: Wed, 23 Jul 2025 23:55:31 +0200 Subject: [PATCH 06/20] fix: non-optional x/y target calculation for ld2450 (#9849) --- esphome/components/ld2450/ld2450.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/ld2450/ld2450.cpp b/esphome/components/ld2450/ld2450.cpp index 8f3b3a3f21..09761b2937 100644 --- a/esphome/components/ld2450/ld2450.cpp +++ b/esphome/components/ld2450/ld2450.cpp @@ -477,10 +477,11 @@ void LD2450Component::handle_periodic_data_() { // X start = TARGET_X + index * 8; is_moving = false; + // tx is used for further calculations, so always needs to be populated + val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]); + tx = val; sensor::Sensor *sx = this->move_x_sensors_[index]; if (sx != nullptr) { - val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]); - tx = val; if (this->cached_target_data_[index].x != val) { sx->publish_state(val); this->cached_target_data_[index].x = val; @@ -488,10 +489,11 @@ void LD2450Component::handle_periodic_data_() { } // Y start = TARGET_Y + index * 8; + // ty is used for further calculations, so always needs to be populated + val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]); + ty = val; sensor::Sensor *sy = this->move_y_sensors_[index]; if (sy != nullptr) { - val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]); - ty = val; if (this->cached_target_data_[index].y != val) { sy->publish_state(val); this->cached_target_data_[index].y = val; From f9534fbd5d5117c2cd0f962da2e462036ab8909e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 24 Jul 2025 08:03:36 +1000 Subject: [PATCH 07/20] [interval] Fix startup behaviour (#9793) --- esphome/components/interval/interval.h | 14 +++++--------- esphome/core/component.cpp | 5 ++--- esphome/core/scheduler.cpp | 8 ++++++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/esphome/components/interval/interval.h b/esphome/components/interval/interval.h index 8f904b104d..e419841e6c 100644 --- a/esphome/components/interval/interval.h +++ b/esphome/components/interval/interval.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/log.h" #include "esphome/core/automation.h" namespace esphome { @@ -8,16 +9,12 @@ namespace interval { class IntervalTrigger : public Trigger<>, public PollingComponent { public: - void update() override { - if (this->started_) - this->trigger(); - } + void update() override { this->trigger(); } void setup() override { - if (this->startup_delay_ == 0) { - this->started_ = true; - } else { - this->set_timeout(this->startup_delay_, [this] { this->started_ = true; }); + if (this->startup_delay_ != 0) { + this->stop_poller(); + this->set_timeout(this->startup_delay_, [this] { this->start_poller(); }); } } @@ -25,7 +22,6 @@ class IntervalTrigger : public Trigger<>, public PollingComponent { protected: uint32_t startup_delay_{0}; - bool started_{false}; }; } // namespace interval diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index aec6c17786..a6a59d30d5 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -373,11 +373,10 @@ bool Component::has_overridden_loop() const { PollingComponent::PollingComponent(uint32_t update_interval) : update_interval_(update_interval) {} void PollingComponent::call_setup() { + // init the poller before calling setup, allowing setup to cancel it if desired + this->start_poller(); // Let the polling component subclass setup their HW. this->setup(); - - // init the poller - this->start_poller(); } void PollingComponent::start_poller() { diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 9e66fd3432..fc5d43d262 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -17,6 +17,8 @@ static const char *const TAG = "scheduler"; static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10; // Half the 32-bit range - used to detect rollovers vs normal time progression static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits::max() / 2; +// max delay to start an interval sequence +static constexpr uint32_t MAX_INTERVAL_DELAY = 5000; // Uncomment to debug scheduler // #define ESPHOME_DEBUG_SCHEDULER @@ -100,9 +102,11 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type // Type-specific setup if (type == SchedulerItem::INTERVAL) { item->interval = delay; - // Calculate random offset (0 to interval/2) - uint32_t offset = (delay != 0) ? (random_uint32() % delay) / 2 : 0; + // first execution happens immediately after a random smallish offset + // Calculate random offset (0 to min(interval/2, 5s)) + uint32_t offset = (uint32_t) (std::min(delay / 2, MAX_INTERVAL_DELAY) * random_float()); item->next_execution_ = now + offset; + ESP_LOGV(TAG, "Scheduler interval for %s is %" PRIu32 "ms, offset %" PRIu32 "ms", name_cstr, delay, offset); } else { item->interval = 0; item->next_execution_ = now + delay; From 3960e2bae77507d1b89ca1cccbcf22f3d2c15648 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:27:05 +1000 Subject: [PATCH 08/20] [mipi] Refactor constants and functions (#9853) --- esphome/components/const/__init__.py | 4 + .../components/esp32_rmt_led_strip/light.py | 2 +- esphome/components/lvgl/__init__.py | 10 +- esphome/components/lvgl/defines.py | 1 - esphome/components/mipi/__init__.py | 403 ++++++++++++++++++ esphome/components/mipi_spi/__init__.py | 7 - esphome/components/mipi_spi/display.py | 251 ++--------- .../components/mipi_spi/models/__init__.py | 65 --- esphome/components/mipi_spi/models/amoled.py | 18 +- .../components/mipi_spi/models/commands.py | 82 ---- esphome/components/mipi_spi/models/ili.py | 10 +- esphome/components/mipi_spi/models/jc.py | 4 +- esphome/components/mipi_spi/models/lilygo.py | 2 +- .../components/mipi_spi/models/waveshare.py | 2 +- esphome/components/rpi_dpi_rgb/display.py | 24 +- .../components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 1 - esphome/components/st7701s/display.py | 20 +- esphome/components/st7701s/st7701s.cpp | 1 - tests/component_tests/mipi_spi/test_init.py | 2 +- 19 files changed, 488 insertions(+), 421 deletions(-) create mode 100644 esphome/components/mipi/__init__.py delete mode 100644 esphome/components/mipi_spi/models/commands.py diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 18ef4d48b6..19924f0da7 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -3,8 +3,12 @@ CODEOWNERS = ["@esphome/core"] CONF_BYTE_ORDER = "byte_order" +BYTE_ORDER_LITTLE = "little_endian" +BYTE_ORDER_BIG = "big_endian" + CONF_COLOR_DEPTH = "color_depth" CONF_DRAW_ROUNDING = "draw_rounding" CONF_ON_RECEIVE = "on_receive" CONF_ON_STATE_CHANGE = "on_state_change" CONF_REQUEST_HEADERS = "request_headers" +CONF_USE_PSRAM = "use_psram" diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 33ae44e435..ac4f0b2e92 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -4,6 +4,7 @@ import logging from esphome import pins import esphome.codegen as cg from esphome.components import esp32, light +from esphome.components.const import CONF_USE_PSRAM import esphome.config_validation as cv from esphome.const import ( CONF_CHIPSET, @@ -57,7 +58,6 @@ CHIPSETS = { "SM16703": LEDStripTimings(300, 900, 900, 300, 0, 0), } -CONF_USE_PSRAM = "use_psram" CONF_IS_WRGB = "is_wrgb" CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 4a450375c4..b1879e6314 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -2,7 +2,7 @@ import logging from esphome.automation import build_automation, register_action, validate_automation import esphome.codegen as cg -from esphome.components.const import CONF_DRAW_ROUNDING +from esphome.components.const import CONF_COLOR_DEPTH, CONF_DRAW_ROUNDING from esphome.components.display import Display import esphome.config_validation as cv from esphome.const import ( @@ -186,7 +186,7 @@ def multi_conf_validate(configs: list[dict]): for config in configs[1:]: for item in ( df.CONF_LOG_LEVEL, - df.CONF_COLOR_DEPTH, + CONF_COLOR_DEPTH, df.CONF_BYTE_ORDER, df.CONF_TRANSPARENCY_KEY, ): @@ -275,11 +275,11 @@ async def to_code(configs): "LVGL_LOG_LEVEL", cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config_0[df.CONF_LOG_LEVEL]}"), ) - add_define("LV_COLOR_DEPTH", config_0[df.CONF_COLOR_DEPTH]) + add_define("LV_COLOR_DEPTH", config_0[CONF_COLOR_DEPTH]) for font in helpers.lv_fonts_used: add_define(f"LV_FONT_{font.upper()}") - if config_0[df.CONF_COLOR_DEPTH] == 16: + if config_0[CONF_COLOR_DEPTH] == 16: add_define( "LV_COLOR_16_SWAP", "1" if config_0[df.CONF_BYTE_ORDER] == "big_endian" else "0", @@ -416,7 +416,7 @@ LVGL_SCHEMA = cv.All( { cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), cv.GenerateID(df.CONF_DISPLAYS): display_schema, - cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16), + cv.Optional(CONF_COLOR_DEPTH, default=16): cv.one_of(16), cv.Optional( df.CONF_DEFAULT_FONT, default="montserrat_14" ): lvalid.lv_font, diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index baa9a19c51..206a3d1622 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -418,7 +418,6 @@ CONF_BUTTONS = "buttons" CONF_BYTE_ORDER = "byte_order" CONF_CHANGE_RATE = "change_rate" CONF_CLOSE_BUTTON = "close_button" -CONF_COLOR_DEPTH = "color_depth" CONF_CONTROL = "control" CONF_DEFAULT_FONT = "default_font" CONF_DEFAULT_GROUP = "default_group" diff --git a/esphome/components/mipi/__init__.py b/esphome/components/mipi/__init__.py new file mode 100644 index 0000000000..a6cb8f31eb --- /dev/null +++ b/esphome/components/mipi/__init__.py @@ -0,0 +1,403 @@ +# Various constants used in MIPI DBI communication +# Various configuration constants for MIPI displays +# Various utility functions for MIPI DBI configuration + +from typing import Any + +from esphome.components.const import CONF_COLOR_DEPTH +from esphome.components.display import CONF_SHOW_TEST_CARD, display_ns +import esphome.config_validation as cv +from esphome.const import ( + CONF_BRIGHTNESS, + CONF_COLOR_ORDER, + CONF_DIMENSIONS, + CONF_HEIGHT, + CONF_INIT_SEQUENCE, + CONF_INVERT_COLORS, + CONF_LAMBDA, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_OFFSET_HEIGHT, + CONF_OFFSET_WIDTH, + CONF_PAGES, + CONF_ROTATION, + CONF_SWAP_XY, + CONF_TRANSFORM, + CONF_WIDTH, +) +from esphome.core import TimePeriod + +LOGGER = cv.logging.getLogger(__name__) + +ColorOrder = display_ns.enum("ColorMode") + +NOP = 0x00 +SWRESET = 0x01 +RDDID = 0x04 +RDDST = 0x09 +RDMODE = 0x0A +RDMADCTL = 0x0B +RDPIXFMT = 0x0C +RDIMGFMT = 0x0D +RDSELFDIAG = 0x0F +SLEEP_IN = 0x10 +SLPIN = 0x10 +SLEEP_OUT = 0x11 +SLPOUT = 0x11 +PTLON = 0x12 +NORON = 0x13 +INVERT_OFF = 0x20 +INVOFF = 0x20 +INVERT_ON = 0x21 +INVON = 0x21 +ALL_ON = 0x23 +WRAM = 0x24 +GAMMASET = 0x26 +MIPI = 0x26 +DISPOFF = 0x28 +DISPON = 0x29 +CASET = 0x2A +PASET = 0x2B +RASET = 0x2B +RAMWR = 0x2C +WDATA = 0x2C +RAMRD = 0x2E +PTLAR = 0x30 +VSCRDEF = 0x33 +TEON = 0x35 +MADCTL = 0x36 +MADCTL_CMD = 0x36 +VSCRSADD = 0x37 +IDMOFF = 0x38 +IDMON = 0x39 +COLMOD = 0x3A +PIXFMT = 0x3A +GETSCANLINE = 0x45 +BRIGHTNESS = 0x51 +WRDISBV = 0x51 +RDDISBV = 0x52 +WRCTRLD = 0x53 +SWIRE1 = 0x5A +SWIRE2 = 0x5B +IFMODE = 0xB0 +FRMCTR1 = 0xB1 +FRMCTR2 = 0xB2 +FRMCTR3 = 0xB3 +INVCTR = 0xB4 +DFUNCTR = 0xB6 +ETMOD = 0xB7 +PWCTR1 = 0xC0 +PWCTR2 = 0xC1 +PWCTR3 = 0xC2 +PWCTR4 = 0xC3 +PWCTR5 = 0xC4 +VMCTR1 = 0xC5 +IFCTR = 0xC6 +VMCTR2 = 0xC7 +GMCTR = 0xC8 +SETEXTC = 0xC8 +PWSET = 0xD0 +VMCTR = 0xD1 +PWSETN = 0xD2 +RDID4 = 0xD3 +RDINDEX = 0xD9 +RDID1 = 0xDA +RDID2 = 0xDB +RDID3 = 0xDC +RDIDX = 0xDD +GMCTRP1 = 0xE0 +GMCTRN1 = 0xE1 +CSCON = 0xF0 +PWCTR6 = 0xF6 +ADJCTL3 = 0xF7 +PAGESEL = 0xFE + +MADCTL_MY = 0x80 # Bit 7 Bottom to top +MADCTL_MX = 0x40 # Bit 6 Right to left +MADCTL_MV = 0x20 # Bit 5 Reverse Mode +MADCTL_ML = 0x10 # Bit 4 LCD refresh Bottom to top +MADCTL_RGB = 0x00 # Bit 3 Red-Green-Blue pixel order +MADCTL_BGR = 0x08 # Bit 3 Blue-Green-Red pixel order +MADCTL_MH = 0x04 # Bit 2 LCD refresh right to left + +# These bits are used instead of the above bits on some chips, where using MX and MY results in incorrect +# partial updates. +MADCTL_XFLIP = 0x02 # Mirror the display horizontally +MADCTL_YFLIP = 0x01 # Mirror the display vertically + +# Special constant for delays in command sequences +DELAY_FLAG = 0xFFF # Special flag to indicate a delay + +CONF_PIXEL_MODE = "pixel_mode" +CONF_USE_AXIS_FLIPS = "use_axis_flips" + +PIXEL_MODE_24BIT = "24bit" +PIXEL_MODE_18BIT = "18bit" +PIXEL_MODE_16BIT = "16bit" + +PIXEL_MODES = { + PIXEL_MODE_16BIT: 0x55, + PIXEL_MODE_18BIT: 0x66, + PIXEL_MODE_24BIT: 0x77, +} + +MODE_RGB = "RGB" +MODE_BGR = "BGR" +COLOR_ORDERS = { + MODE_RGB: ColorOrder.COLOR_ORDER_RGB, + MODE_BGR: ColorOrder.COLOR_ORDER_BGR, +} + +CONF_HSYNC_BACK_PORCH = "hsync_back_porch" +CONF_HSYNC_FRONT_PORCH = "hsync_front_porch" +CONF_HSYNC_PULSE_WIDTH = "hsync_pulse_width" +CONF_VSYNC_BACK_PORCH = "vsync_back_porch" +CONF_VSYNC_FRONT_PORCH = "vsync_front_porch" +CONF_VSYNC_PULSE_WIDTH = "vsync_pulse_width" +CONF_PCLK_FREQUENCY = "pclk_frequency" +CONF_PCLK_INVERTED = "pclk_inverted" +CONF_NATIVE_HEIGHT = "native_height" +CONF_NATIVE_WIDTH = "native_width" + +CONF_DE_PIN = "de_pin" +CONF_PCLK_PIN = "pclk_pin" + + +def power_of_two(value): + value = cv.int_range(1, 128)(value) + if value & (value - 1) != 0: + raise cv.Invalid("value must be a power of two") + return value + + +def validate_dimension(rounding): + def validator(value): + value = cv.positive_int(value) + if value % rounding != 0: + raise cv.Invalid(f"Dimensions and offsets must be divisible by {rounding}") + return value + + return validator + + +def dimension_schema(rounding): + return cv.Any( + cv.dimensions, + cv.Schema( + { + cv.Required(CONF_WIDTH): validate_dimension(rounding), + cv.Required(CONF_HEIGHT): validate_dimension(rounding), + cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension( + rounding + ), + cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension(rounding), + } + ), + ) + + +def map_sequence(value): + """ + Maps one entry in a sequence to a command and data bytes. + The format is a repeated sequence of [CMD, ] where is s a sequence of bytes. The length is inferred + from the length of the sequence and should not be explicit. + A single integer can be provided where there are no data bytes, in which case it is treated as a command. + A delay can be inserted by specifying "- delay N" where N is in ms + """ + if isinstance(value, str) and value.lower().startswith("delay "): + value = value.lower()[6:] + delay_value = cv.All( + cv.positive_time_period_milliseconds, + cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)), + )(value) + return DELAY_FLAG, delay_value.total_milliseconds + value = cv.All(cv.ensure_list(cv.int_range(0, 255)), cv.Length(1, 254))(value) + return tuple(value) + + +def delay(ms): + return DELAY_FLAG, ms + + +class DriverChip: + models = {} + + def __init__( + self, + name: str, + initsequence=None, + **defaults, + ): + name = name.upper() + self.name = name + self.initsequence = initsequence + self.defaults = defaults + DriverChip.models[name] = self + + def extend(self, name, **kwargs) -> "DriverChip": + defaults = self.defaults.copy() + if ( + CONF_WIDTH in defaults + and CONF_OFFSET_WIDTH in kwargs + and CONF_NATIVE_WIDTH not in defaults + ): + defaults[CONF_NATIVE_WIDTH] = defaults[CONF_WIDTH] + if ( + CONF_HEIGHT in defaults + and CONF_OFFSET_HEIGHT in kwargs + and CONF_NATIVE_HEIGHT not in defaults + ): + defaults[CONF_NATIVE_HEIGHT] = defaults[CONF_HEIGHT] + defaults.update(kwargs) + return DriverChip(name, initsequence=self.initsequence, **defaults) + + def get_default(self, key, fallback: Any = False) -> Any: + return self.defaults.get(key, fallback) + + def option(self, name, fallback=False) -> cv.Optional: + return cv.Optional(name, default=self.get_default(name, fallback)) + + def rotation_as_transform(self, config) -> bool: + """ + Check if a rotation can be implemented in hardware using the MADCTL register. + A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y. + """ + rotation = config.get(CONF_ROTATION, 0) + return rotation and ( + self.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180 + ) + + def get_dimensions(self, config) -> tuple[int, int, int, int]: + if CONF_DIMENSIONS in config: + # Explicit dimensions, just use as is + dimensions = config[CONF_DIMENSIONS] + if isinstance(dimensions, dict): + width = dimensions[CONF_WIDTH] + height = dimensions[CONF_HEIGHT] + offset_width = dimensions[CONF_OFFSET_WIDTH] + offset_height = dimensions[CONF_OFFSET_HEIGHT] + return width, height, offset_width, offset_height + (width, height) = dimensions + return width, height, 0, 0 + + # Default dimensions, use model defaults + transform = self.get_transform(config) + + width = self.get_default(CONF_WIDTH) + height = self.get_default(CONF_HEIGHT) + offset_width = self.get_default(CONF_OFFSET_WIDTH, 0) + offset_height = self.get_default(CONF_OFFSET_HEIGHT, 0) + + # if mirroring axes and there are offsets, also mirror the offsets to cater for situations where + # the offset is asymmetric + if transform[CONF_MIRROR_X]: + native_width = self.get_default(CONF_NATIVE_WIDTH, width + offset_width * 2) + offset_width = native_width - width - offset_width + if transform[CONF_MIRROR_Y]: + native_height = self.get_default( + CONF_NATIVE_HEIGHT, height + offset_height * 2 + ) + offset_height = native_height - height - offset_height + # Swap default dimensions if swap_xy is set + if transform[CONF_SWAP_XY] is True: + width, height = height, width + offset_height, offset_width = offset_width, offset_height + return width, height, offset_width, offset_height + + def get_transform(self, config) -> dict[str, bool]: + can_transform = self.rotation_as_transform(config) + transform = config.get( + CONF_TRANSFORM, + { + CONF_MIRROR_X: self.get_default(CONF_MIRROR_X, False), + CONF_MIRROR_Y: self.get_default(CONF_MIRROR_Y, False), + CONF_SWAP_XY: self.get_default(CONF_SWAP_XY, False), + }, + ) + + # Can we use the MADCTL register to set the rotation? + if can_transform and CONF_TRANSFORM not in config: + rotation = config[CONF_ROTATION] + if rotation == 180: + transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X] + transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y] + elif rotation == 90: + transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY] + transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X] + else: + transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY] + transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y] + transform[CONF_TRANSFORM] = True + return transform + + def get_sequence(self, config) -> tuple[tuple[int, ...], int]: + """ + Create the init sequence for the display. + Use the default sequence from the model, if any, and append any custom sequence provided in the config. + Append SLPOUT (if not already in the sequence) and DISPON to the end of the sequence + Pixel format, color order, and orientation will be set. + Returns a tuple of the init sequence and the computed MADCTL value. + """ + sequence = list(self.initsequence) + custom_sequence = config.get(CONF_INIT_SEQUENCE, []) + sequence.extend(custom_sequence) + # Ensure each command is a tuple + sequence = [x if isinstance(x, tuple) else (x,) for x in sequence] + + # Set pixel format if not already in the custom sequence + pixel_mode = config[CONF_PIXEL_MODE] + if not isinstance(pixel_mode, int): + pixel_mode = PIXEL_MODES[pixel_mode] + sequence.append((PIXFMT, pixel_mode)) + + # Does the chip use the flipping bits for mirroring rather than the reverse order bits? + use_flip = config.get(CONF_USE_AXIS_FLIPS) + madctl = 0 + transform = self.get_transform(config) + if self.rotation_as_transform(config): + LOGGER.info("Using hardware transform to implement rotation") + if transform.get(CONF_MIRROR_X): + madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX + if transform.get(CONF_MIRROR_Y): + madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY + if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined + madctl |= MADCTL_MV + if config[CONF_COLOR_ORDER] == MODE_BGR: + madctl |= MADCTL_BGR + sequence.append((MADCTL, madctl)) + if config[CONF_INVERT_COLORS]: + sequence.append((INVON,)) + else: + sequence.append((INVOFF,)) + if brightness := config.get(CONF_BRIGHTNESS, self.get_default(CONF_BRIGHTNESS)): + sequence.append((BRIGHTNESS, brightness)) + sequence.append((SLPOUT,)) + sequence.append((DISPON,)) + + # Flatten the sequence into a list of bytes, with the length of each command + # or the delay flag inserted where needed + return sum( + tuple( + (x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:] + for x in sequence + ), + (), + ), madctl + + +def requires_buffer(config) -> bool: + """ + Check if the display configuration requires a buffer. It will do so if any drawing methods are configured. + :param config: + :return: True if a buffer is required, False otherwise + """ + return any( + config.get(key) for key in (CONF_LAMBDA, CONF_PAGES, CONF_SHOW_TEST_CARD) + ) + + +def get_color_depth(config) -> int: + """ + Get the color depth in bits from the configuration. + """ + return int(config[CONF_COLOR_DEPTH].removesuffix("bit")) diff --git a/esphome/components/mipi_spi/__init__.py b/esphome/components/mipi_spi/__init__.py index 879efda619..f0f02aedd8 100644 --- a/esphome/components/mipi_spi/__init__.py +++ b/esphome/components/mipi_spi/__init__.py @@ -3,11 +3,4 @@ CODEOWNERS = ["@clydebarrow"] DOMAIN = "mipi_spi" CONF_SPI_16 = "spi_16" -CONF_PIXEL_MODE = "pixel_mode" CONF_BUS_MODE = "bus_mode" -CONF_USE_AXIS_FLIPS = "use_axis_flips" -CONF_NATIVE_WIDTH = "native_width" -CONF_NATIVE_HEIGHT = "native_height" - -MODE_RGB = "RGB" -MODE_BGR = "BGR" diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index d25dfd8539..d5c9d7aa0f 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -9,6 +9,20 @@ from esphome.components.const import ( CONF_DRAW_ROUNDING, ) from esphome.components.display import CONF_SHOW_TEST_CARD, DISPLAY_ROTATIONS +from esphome.components.mipi import ( + CONF_PIXEL_MODE, + CONF_USE_AXIS_FLIPS, + MADCTL, + MODE_BGR, + MODE_RGB, + PIXFMT, + DriverChip, + dimension_schema, + get_color_depth, + map_sequence, + power_of_two, + requires_buffer, +) from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE import esphome.config_validation as cv from esphome.config_validation import ALLOW_EXTRA @@ -21,7 +35,6 @@ from esphome.const import ( CONF_DC_PIN, CONF_DIMENSIONS, CONF_ENABLE_PIN, - CONF_HEIGHT, CONF_ID, CONF_INIT_SEQUENCE, CONF_INVERT_COLORS, @@ -29,49 +42,18 @@ from esphome.const import ( CONF_MIRROR_X, CONF_MIRROR_Y, CONF_MODEL, - CONF_OFFSET_HEIGHT, - CONF_OFFSET_WIDTH, - CONF_PAGES, CONF_RESET_PIN, CONF_ROTATION, CONF_SWAP_XY, CONF_TRANSFORM, CONF_WIDTH, ) -from esphome.core import CORE, TimePeriod +from esphome.core import CORE from esphome.cpp_generator import TemplateArguments from esphome.final_validate import full_config -from . import ( - CONF_BUS_MODE, - CONF_NATIVE_HEIGHT, - CONF_NATIVE_WIDTH, - CONF_PIXEL_MODE, - CONF_SPI_16, - CONF_USE_AXIS_FLIPS, - DOMAIN, - MODE_BGR, - MODE_RGB, -) -from .models import ( - DELAY_FLAG, - MADCTL_BGR, - MADCTL_MV, - MADCTL_MX, - MADCTL_MY, - MADCTL_XFLIP, - MADCTL_YFLIP, - DriverChip, - adafruit, - amoled, - cyd, - ili, - jc, - lanbon, - lilygo, - waveshare, -) -from .models.commands import BRIGHTNESS, DISPON, INVOFF, INVON, MADCTL, PIXFMT, SLPOUT +from . import CONF_BUS_MODE, CONF_SPI_16, DOMAIN +from .models import adafruit, amoled, cyd, ili, jc, lanbon, lilygo, waveshare DEPENDENCIES = ["spi"] @@ -124,45 +106,6 @@ DISPLAY_PIXEL_MODES = { } -def get_dimensions(config): - if CONF_DIMENSIONS in config: - # Explicit dimensions, just use as is - dimensions = config[CONF_DIMENSIONS] - if isinstance(dimensions, dict): - width = dimensions[CONF_WIDTH] - height = dimensions[CONF_HEIGHT] - offset_width = dimensions[CONF_OFFSET_WIDTH] - offset_height = dimensions[CONF_OFFSET_HEIGHT] - return width, height, offset_width, offset_height - (width, height) = dimensions - return width, height, 0, 0 - - # Default dimensions, use model defaults - transform = get_transform(config) - - model = MODELS[config[CONF_MODEL]] - width = model.get_default(CONF_WIDTH) - height = model.get_default(CONF_HEIGHT) - offset_width = model.get_default(CONF_OFFSET_WIDTH, 0) - offset_height = model.get_default(CONF_OFFSET_HEIGHT, 0) - - # if mirroring axes and there are offsets, also mirror the offsets to cater for situations where - # the offset is asymmetric - if transform[CONF_MIRROR_X]: - native_width = model.get_default(CONF_NATIVE_WIDTH, width + offset_width * 2) - offset_width = native_width - width - offset_width - if transform[CONF_MIRROR_Y]: - native_height = model.get_default( - CONF_NATIVE_HEIGHT, height + offset_height * 2 - ) - offset_height = native_height - height - offset_height - # Swap default dimensions if swap_xy is set - if transform[CONF_SWAP_XY] is True: - width, height = height, width - offset_height, offset_width = offset_width, offset_height - return width, height, offset_width, offset_height - - def denominator(config): """ Calculate the best denominator for a buffer size fraction. @@ -171,10 +114,11 @@ def denominator(config): :config: The configuration dictionary containing the buffer size fraction and display dimensions :return: The denominator to use for the buffer size fraction """ + model = MODELS[config[CONF_MODEL]] frac = config.get(CONF_BUFFER_SIZE) if frac is None or frac > 0.75: return 1 - height, _width, _offset_width, _offset_height = get_dimensions(config) + height, _width, _offset_width, _offset_height = model.get_dimensions(config) try: return next(x for x in range(2, 17) if frac >= 1 / x and height % x == 0) except StopIteration: @@ -183,58 +127,6 @@ def denominator(config): ) from StopIteration -def validate_dimension(rounding): - def validator(value): - value = cv.positive_int(value) - if value % rounding != 0: - raise cv.Invalid(f"Dimensions and offsets must be divisible by {rounding}") - return value - - return validator - - -def map_sequence(value): - """ - The format is a repeated sequence of [CMD, ] where is s a sequence of bytes. The length is inferred - from the length of the sequence and should not be explicit. - A delay can be inserted by specifying "- delay N" where N is in ms - """ - if isinstance(value, str) and value.lower().startswith("delay "): - value = value.lower()[6:] - delay = cv.All( - cv.positive_time_period_milliseconds, - cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)), - )(value) - return DELAY_FLAG, delay.total_milliseconds - if isinstance(value, int): - return (value,) - value = cv.All(cv.ensure_list(cv.int_range(0, 255)), cv.Length(1, 254))(value) - return tuple(value) - - -def power_of_two(value): - value = cv.int_range(1, 128)(value) - if value & (value - 1) != 0: - raise cv.Invalid("value must be a power of two") - return value - - -def dimension_schema(rounding): - return cv.Any( - cv.dimensions, - cv.Schema( - { - cv.Required(CONF_WIDTH): validate_dimension(rounding), - cv.Required(CONF_HEIGHT): validate_dimension(rounding), - cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension( - rounding - ), - cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension(rounding), - } - ), - ) - - def swap_xy_schema(model): uses_swap = model.get_default(CONF_SWAP_XY, None) != cv.UNDEFINED @@ -250,7 +142,7 @@ def swap_xy_schema(model): def model_schema(config): model = MODELS[config[CONF_MODEL]] - bus_mode = config.get(CONF_BUS_MODE, model.modes[0]) + bus_mode = config[CONF_BUS_MODE] transform = cv.Schema( { cv.Required(CONF_MIRROR_X): cv.boolean, @@ -340,18 +232,6 @@ def model_schema(config): return schema -def is_rotation_transformable(config): - """ - Check if a rotation can be implemented in hardware using the MADCTL register. - A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y. - """ - model = MODELS[config[CONF_MODEL]] - rotation = config.get(CONF_ROTATION, 0) - return rotation and ( - model.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180 - ) - - def customise_schema(config): """ Create a customised config schema for a specific model and validate the configuration. @@ -367,7 +247,7 @@ def customise_schema(config): extra=ALLOW_EXTRA, )(config) model = MODELS[config[CONF_MODEL]] - bus_modes = model.modes + bus_modes = (TYPE_SINGLE, TYPE_QUAD, TYPE_OCTAL) config = cv.Schema( { model.option(CONF_BUS_MODE, TYPE_SINGLE): cv.one_of(*bus_modes, lower=True), @@ -375,7 +255,7 @@ def customise_schema(config): }, extra=ALLOW_EXTRA, )(config) - bus_mode = config.get(CONF_BUS_MODE, model.modes[0]) + bus_mode = config[CONF_BUS_MODE] config = model_schema(config)(config) # Check for invalid combinations of MADCTL config if init_sequence := config.get(CONF_INIT_SEQUENCE): @@ -400,23 +280,9 @@ def customise_schema(config): CONFIG_SCHEMA = customise_schema -def requires_buffer(config): - """ - Check if the display configuration requires a buffer. It will do so if any drawing methods are configured. - :param config: - :return: True if a buffer is required, False otherwise - """ - return any( - config.get(key) for key in (CONF_LAMBDA, CONF_PAGES, CONF_SHOW_TEST_CARD) - ) - - -def get_color_depth(config): - return int(config[CONF_COLOR_DEPTH].removesuffix("bit")) - - def _final_validate(config): global_config = full_config.get() + model = MODELS[config[CONF_MODEL]] from esphome.components.lvgl import DOMAIN as LVGL_DOMAIN @@ -433,7 +299,7 @@ def _final_validate(config): return config color_depth = get_color_depth(config) frac = denominator(config) - height, width, _offset_width, _offset_height = get_dimensions(config) + height, width, _offset_width, _offset_height = model.get_dimensions(config) buffer_size = color_depth // 8 * width * height // frac # Target a buffer size of 20kB @@ -463,7 +329,7 @@ def get_transform(config): :return: """ model = MODELS[config[CONF_MODEL]] - can_transform = is_rotation_transformable(config) + can_transform = model.rotation_as_transform(config) transform = config.get( CONF_TRANSFORM, { @@ -489,63 +355,6 @@ def get_transform(config): return transform -def get_sequence(model, config): - """ - Create the init sequence for the display. - Use the default sequence from the model, if any, and append any custom sequence provided in the config. - Append SLPOUT (if not already in the sequence) and DISPON to the end of the sequence - Pixel format, color order, and orientation will be set. - """ - sequence = list(model.initsequence) - custom_sequence = config.get(CONF_INIT_SEQUENCE, []) - sequence.extend(custom_sequence) - # Ensure each command is a tuple - sequence = [x if isinstance(x, tuple) else (x,) for x in sequence] - commands = [x[0] for x in sequence] - # Set pixel format if not already in the custom sequence - pixel_mode = DISPLAY_PIXEL_MODES[config[CONF_PIXEL_MODE]] - sequence.append((PIXFMT, pixel_mode[0])) - # Does the chip use the flipping bits for mirroring rather than the reverse order bits? - use_flip = config[CONF_USE_AXIS_FLIPS] - if MADCTL not in commands: - madctl = 0 - transform = get_transform(config) - if transform.get(CONF_TRANSFORM): - LOGGER.info("Using hardware transform to implement rotation") - if transform.get(CONF_MIRROR_X): - madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX - if transform.get(CONF_MIRROR_Y): - madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY - if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined - madctl |= MADCTL_MV - if config[CONF_COLOR_ORDER] == MODE_BGR: - madctl |= MADCTL_BGR - sequence.append((MADCTL, madctl)) - if INVON not in commands and INVOFF not in commands: - if config[CONF_INVERT_COLORS]: - sequence.append((INVON,)) - else: - sequence.append((INVOFF,)) - if BRIGHTNESS not in commands: - if brightness := config.get( - CONF_BRIGHTNESS, model.get_default(CONF_BRIGHTNESS) - ): - sequence.append((BRIGHTNESS, brightness)) - if SLPOUT not in commands: - sequence.append((SLPOUT,)) - sequence.append((DISPON,)) - - # Flatten the sequence into a list of bytes, with the length of each command - # or the delay flag inserted where needed - return sum( - tuple( - (x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:] - for x in sequence - ), - (), - ) - - def get_instance(config): """ Get the type of MipiSpi instance to create based on the configuration, @@ -553,7 +362,8 @@ def get_instance(config): :param config: :return: type, template arguments """ - width, height, offset_width, offset_height = get_dimensions(config) + model = MODELS[config[CONF_MODEL]] + width, height, offset_width, offset_height = model.get_dimensions(config) color_depth = int(config[CONF_COLOR_DEPTH].removesuffix("bit")) bufferpixels = COLOR_DEPTHS[color_depth] @@ -568,7 +378,7 @@ def get_instance(config): buffer_type = cg.uint8 if color_depth == 8 else cg.uint16 frac = denominator(config) rotation = DISPLAY_ROTATIONS[ - 0 if is_rotation_transformable(config) else config.get(CONF_ROTATION, 0) + 0 if model.rotation_as_transform(config) else config.get(CONF_ROTATION, 0) ] templateargs = [ buffer_type, @@ -594,8 +404,9 @@ async def to_code(config): var_id = config[CONF_ID] var_id.type, templateargs = get_instance(config) var = cg.new_Pvariable(var_id, TemplateArguments(*templateargs)) - cg.add(var.set_init_sequence(get_sequence(model, config))) - if is_rotation_transformable(config): + init_sequence, _madctl = model.get_sequence(config) + cg.add(var.set_init_sequence(init_sequence)) + if model.rotation_as_transform(config): if CONF_TRANSFORM in config: LOGGER.warning("Use of 'transform' with 'rotation' is not recommended") else: diff --git a/esphome/components/mipi_spi/models/__init__.py b/esphome/components/mipi_spi/models/__init__.py index e9726032d4..e69de29bb2 100644 --- a/esphome/components/mipi_spi/models/__init__.py +++ b/esphome/components/mipi_spi/models/__init__.py @@ -1,65 +0,0 @@ -from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE -import esphome.config_validation as cv -from esphome.const import CONF_HEIGHT, CONF_OFFSET_HEIGHT, CONF_OFFSET_WIDTH, CONF_WIDTH - -from .. import CONF_NATIVE_HEIGHT, CONF_NATIVE_WIDTH - -MADCTL_MY = 0x80 # Bit 7 Bottom to top -MADCTL_MX = 0x40 # Bit 6 Right to left -MADCTL_MV = 0x20 # Bit 5 Reverse Mode -MADCTL_ML = 0x10 # Bit 4 LCD refresh Bottom to top -MADCTL_RGB = 0x00 # Bit 3 Red-Green-Blue pixel order -MADCTL_BGR = 0x08 # Bit 3 Blue-Green-Red pixel order -MADCTL_MH = 0x04 # Bit 2 LCD refresh right to left - -# These bits are used instead of the above bits on some chips, where using MX and MY results in incorrect -# partial updates. -MADCTL_XFLIP = 0x02 # Mirror the display horizontally -MADCTL_YFLIP = 0x01 # Mirror the display vertically - -DELAY_FLAG = 0xFFF # Special flag to indicate a delay - - -def delay(ms): - return DELAY_FLAG, ms - - -class DriverChip: - models = {} - - def __init__( - self, - name: str, - modes=(TYPE_SINGLE, TYPE_QUAD, TYPE_OCTAL), - initsequence=None, - **defaults, - ): - name = name.upper() - self.name = name - self.modes = modes - self.initsequence = initsequence - self.defaults = defaults - DriverChip.models[name] = self - - def extend(self, name, **kwargs): - defaults = self.defaults.copy() - if ( - CONF_WIDTH in defaults - and CONF_OFFSET_WIDTH in kwargs - and CONF_NATIVE_WIDTH not in defaults - ): - defaults[CONF_NATIVE_WIDTH] = defaults[CONF_WIDTH] - if ( - CONF_HEIGHT in defaults - and CONF_OFFSET_HEIGHT in kwargs - and CONF_NATIVE_HEIGHT not in defaults - ): - defaults[CONF_NATIVE_HEIGHT] = defaults[CONF_HEIGHT] - defaults.update(kwargs) - return DriverChip(name, self.modes, initsequence=self.initsequence, **defaults) - - def get_default(self, key, fallback=False): - return self.defaults.get(key, fallback) - - def option(self, name, fallback=False): - return cv.Optional(name, default=self.get_default(name, fallback)) diff --git a/esphome/components/mipi_spi/models/amoled.py b/esphome/components/mipi_spi/models/amoled.py index 882d19db30..6fe882b584 100644 --- a/esphome/components/mipi_spi/models/amoled.py +++ b/esphome/components/mipi_spi/models/amoled.py @@ -1,9 +1,19 @@ +from esphome.components.mipi import ( + MIPI, + MODE_RGB, + NORON, + PAGESEL, + PIXFMT, + SLPOUT, + SWIRE1, + SWIRE2, + TEON, + WRAM, + DriverChip, + delay, +) from esphome.components.spi import TYPE_QUAD -from .. import MODE_RGB -from . import DriverChip, delay -from .commands import MIPI, NORON, PAGESEL, PIXFMT, SLPOUT, SWIRE1, SWIRE2, TEON, WRAM - DriverChip( "T-DISPLAY-S3-AMOLED", width=240, diff --git a/esphome/components/mipi_spi/models/commands.py b/esphome/components/mipi_spi/models/commands.py deleted file mode 100644 index 032a6e6b2b..0000000000 --- a/esphome/components/mipi_spi/models/commands.py +++ /dev/null @@ -1,82 +0,0 @@ -# MIPI DBI commands - -NOP = 0x00 -SWRESET = 0x01 -RDDID = 0x04 -RDDST = 0x09 -RDMODE = 0x0A -RDMADCTL = 0x0B -RDPIXFMT = 0x0C -RDIMGFMT = 0x0D -RDSELFDIAG = 0x0F -SLEEP_IN = 0x10 -SLPIN = 0x10 -SLEEP_OUT = 0x11 -SLPOUT = 0x11 -PTLON = 0x12 -NORON = 0x13 -INVERT_OFF = 0x20 -INVOFF = 0x20 -INVERT_ON = 0x21 -INVON = 0x21 -ALL_ON = 0x23 -WRAM = 0x24 -GAMMASET = 0x26 -MIPI = 0x26 -DISPOFF = 0x28 -DISPON = 0x29 -CASET = 0x2A -PASET = 0x2B -RASET = 0x2B -RAMWR = 0x2C -WDATA = 0x2C -RAMRD = 0x2E -PTLAR = 0x30 -VSCRDEF = 0x33 -TEON = 0x35 -MADCTL = 0x36 -MADCTL_CMD = 0x36 -VSCRSADD = 0x37 -IDMOFF = 0x38 -IDMON = 0x39 -COLMOD = 0x3A -PIXFMT = 0x3A -GETSCANLINE = 0x45 -BRIGHTNESS = 0x51 -WRDISBV = 0x51 -RDDISBV = 0x52 -WRCTRLD = 0x53 -SWIRE1 = 0x5A -SWIRE2 = 0x5B -IFMODE = 0xB0 -FRMCTR1 = 0xB1 -FRMCTR2 = 0xB2 -FRMCTR3 = 0xB3 -INVCTR = 0xB4 -DFUNCTR = 0xB6 -ETMOD = 0xB7 -PWCTR1 = 0xC0 -PWCTR2 = 0xC1 -PWCTR3 = 0xC2 -PWCTR4 = 0xC3 -PWCTR5 = 0xC4 -VMCTR1 = 0xC5 -IFCTR = 0xC6 -VMCTR2 = 0xC7 -GMCTR = 0xC8 -SETEXTC = 0xC8 -PWSET = 0xD0 -VMCTR = 0xD1 -PWSETN = 0xD2 -RDID4 = 0xD3 -RDINDEX = 0xD9 -RDID1 = 0xDA -RDID2 = 0xDB -RDID3 = 0xDC -RDIDX = 0xDD -GMCTRP1 = 0xE0 -GMCTRN1 = 0xE1 -CSCON = 0xF0 -PWCTR6 = 0xF6 -ADJCTL3 = 0xF7 -PAGESEL = 0xFE diff --git a/esphome/components/mipi_spi/models/ili.py b/esphome/components/mipi_spi/models/ili.py index cc12b38f5d..0102c0f665 100644 --- a/esphome/components/mipi_spi/models/ili.py +++ b/esphome/components/mipi_spi/models/ili.py @@ -1,8 +1,4 @@ -from esphome.components.spi import TYPE_OCTAL - -from .. import MODE_RGB -from . import DriverChip, delay -from .commands import ( +from esphome.components.mipi import ( ADJCTL3, CSCON, DFUNCTR, @@ -18,6 +14,7 @@ from .commands import ( IFCTR, IFMODE, INVCTR, + MODE_RGB, NORON, PWCTR1, PWCTR2, @@ -32,7 +29,10 @@ from .commands import ( VMCTR1, VMCTR2, VSCRSADD, + DriverChip, + delay, ) +from esphome.components.spi import TYPE_OCTAL DriverChip( "M5CORE", diff --git a/esphome/components/mipi_spi/models/jc.py b/esphome/components/mipi_spi/models/jc.py index 449c5b87ae..f1f046a427 100644 --- a/esphome/components/mipi_spi/models/jc.py +++ b/esphome/components/mipi_spi/models/jc.py @@ -1,10 +1,8 @@ +from esphome.components.mipi import MODE_RGB, DriverChip from esphome.components.spi import TYPE_QUAD import esphome.config_validation as cv from esphome.const import CONF_IGNORE_STRAPPING_WARNING, CONF_NUMBER -from .. import MODE_RGB -from . import DriverChip - AXS15231 = DriverChip( "AXS15231", draw_rounding=8, diff --git a/esphome/components/mipi_spi/models/lilygo.py b/esphome/components/mipi_spi/models/lilygo.py index dd6f9c02f7..13ddc67465 100644 --- a/esphome/components/mipi_spi/models/lilygo.py +++ b/esphome/components/mipi_spi/models/lilygo.py @@ -1,6 +1,6 @@ +from esphome.components.mipi import MODE_BGR from esphome.components.spi import TYPE_OCTAL -from .. import MODE_BGR from .ili import ST7789V, ST7796 ST7789V.extend( diff --git a/esphome/components/mipi_spi/models/waveshare.py b/esphome/components/mipi_spi/models/waveshare.py index 726718aaf6..002f81f3a6 100644 --- a/esphome/components/mipi_spi/models/waveshare.py +++ b/esphome/components/mipi_spi/models/waveshare.py @@ -1,6 +1,6 @@ +from esphome.components.mipi import DriverChip import esphome.config_validation as cv -from . import DriverChip from .ili import ILI9488_A DriverChip( diff --git a/esphome/components/rpi_dpi_rgb/display.py b/esphome/components/rpi_dpi_rgb/display.py index c26143d63e..556ee5eeb4 100644 --- a/esphome/components/rpi_dpi_rgb/display.py +++ b/esphome/components/rpi_dpi_rgb/display.py @@ -2,6 +2,18 @@ from esphome import pins import esphome.codegen as cg from esphome.components import display from esphome.components.esp32 import const, only_on_variant +from esphome.components.mipi import ( + CONF_DE_PIN, + CONF_HSYNC_BACK_PORCH, + CONF_HSYNC_FRONT_PORCH, + CONF_HSYNC_PULSE_WIDTH, + CONF_PCLK_FREQUENCY, + CONF_PCLK_INVERTED, + CONF_PCLK_PIN, + CONF_VSYNC_BACK_PORCH, + CONF_VSYNC_FRONT_PORCH, + CONF_VSYNC_PULSE_WIDTH, +) import esphome.config_validation as cv from esphome.const import ( CONF_BLUE, @@ -27,18 +39,6 @@ from esphome.const import ( DEPENDENCIES = ["esp32"] -CONF_DE_PIN = "de_pin" -CONF_PCLK_PIN = "pclk_pin" - -CONF_HSYNC_FRONT_PORCH = "hsync_front_porch" -CONF_HSYNC_PULSE_WIDTH = "hsync_pulse_width" -CONF_HSYNC_BACK_PORCH = "hsync_back_porch" -CONF_VSYNC_FRONT_PORCH = "vsync_front_porch" -CONF_VSYNC_PULSE_WIDTH = "vsync_pulse_width" -CONF_VSYNC_BACK_PORCH = "vsync_back_porch" -CONF_PCLK_FREQUENCY = "pclk_frequency" -CONF_PCLK_INVERTED = "pclk_inverted" - rpi_dpi_rgb_ns = cg.esphome_ns.namespace("rpi_dpi_rgb") RPI_DPI_RGB = rpi_dpi_rgb_ns.class_("RpiDpiRgb", display.Display, cg.Component) ColorOrder = display.display_ns.enum("ColorMode") diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 91eb947a3e..1706a7e59d 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -23,7 +23,6 @@ void RpiDpiRgb::setup() { config.timings.flags.pclk_active_neg = this->pclk_inverted_; config.timings.pclk_hz = this->pclk_frequency_; config.clk_src = LCD_CLK_SRC_PLL160M; - config.psram_trans_align = 64; size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); for (size_t i = 0; i != data_pin_count; i++) { config.data_gpio_nums[i] = this->data_pins_[i]->get_pin(); diff --git a/esphome/components/st7701s/display.py b/esphome/components/st7701s/display.py index c6ad43c14c..e2452a4c55 100644 --- a/esphome/components/st7701s/display.py +++ b/esphome/components/st7701s/display.py @@ -2,9 +2,17 @@ from esphome import pins import esphome.codegen as cg from esphome.components import display, spi from esphome.components.esp32 import const, only_on_variant -from esphome.components.rpi_dpi_rgb.display import ( +from esphome.components.mipi import ( + CONF_DE_PIN, + CONF_HSYNC_BACK_PORCH, + CONF_HSYNC_FRONT_PORCH, + CONF_HSYNC_PULSE_WIDTH, CONF_PCLK_FREQUENCY, CONF_PCLK_INVERTED, + CONF_PCLK_PIN, + CONF_VSYNC_BACK_PORCH, + CONF_VSYNC_FRONT_PORCH, + CONF_VSYNC_PULSE_WIDTH, ) import esphome.config_validation as cv from esphome.const import ( @@ -36,16 +44,6 @@ from esphome.core import TimePeriod from .init_sequences import ST7701S_INITS, cmd -CONF_DE_PIN = "de_pin" -CONF_PCLK_PIN = "pclk_pin" - -CONF_HSYNC_PULSE_WIDTH = "hsync_pulse_width" -CONF_HSYNC_BACK_PORCH = "hsync_back_porch" -CONF_HSYNC_FRONT_PORCH = "hsync_front_porch" -CONF_VSYNC_PULSE_WIDTH = "vsync_pulse_width" -CONF_VSYNC_BACK_PORCH = "vsync_back_porch" -CONF_VSYNC_FRONT_PORCH = "vsync_front_porch" - DEPENDENCIES = ["spi", "esp32"] st7701s_ns = cg.esphome_ns.namespace("st7701s") diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 46509a7f9f..2af88515c7 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -25,7 +25,6 @@ void ST7701S::setup() { config.timings.flags.pclk_active_neg = this->pclk_inverted_; config.timings.pclk_hz = this->pclk_frequency_; config.clk_src = LCD_CLK_SRC_PLL160M; - config.psram_trans_align = 64; size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); for (size_t i = 0; i != data_pin_count; i++) { config.data_gpio_nums[i] = this->data_pins_[i]->get_pin(); diff --git a/tests/component_tests/mipi_spi/test_init.py b/tests/component_tests/mipi_spi/test_init.py index 9824852653..c4c93866ca 100644 --- a/tests/component_tests/mipi_spi/test_init.py +++ b/tests/component_tests/mipi_spi/test_init.py @@ -16,9 +16,9 @@ from esphome.components.esp32 import ( VARIANTS, ) from esphome.components.esp32.gpio import validate_gpio_pin +from esphome.components.mipi import CONF_NATIVE_HEIGHT from esphome.components.mipi_spi.display import ( CONF_BUS_MODE, - CONF_NATIVE_HEIGHT, CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA, MODELS, From 5cd7f156b93d941e0a475c3654ebf46412e3800d Mon Sep 17 00:00:00 2001 From: Mayur Panchal Date: Thu, 24 Jul 2025 11:34:39 +1000 Subject: [PATCH 09/20] Update post_build.py.script to Fix #7137 (#9578) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32/post_build.py.script | 147 +++++++++++------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/esphome/components/esp32/post_build.py.script b/esphome/components/esp32/post_build.py.script index c181cf30b1..6e0e439011 100644 --- a/esphome/components/esp32/post_build.py.script +++ b/esphome/components/esp32/post_build.py.script @@ -1,77 +1,112 @@ -# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664 - -# pylint: disable=E0602 -Import("env") # noqa +Import("env") import os +import json import shutil +import pathlib +import itertools -if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: - try: - import esptool - except ImportError: - env.Execute("$PYTHONEXE -m pip install esptool") -else: - import subprocess -from SCons.Script import ARGUMENTS +def merge_factory_bin(source, target, env): + """ + Merges all flash sections into a single .factory.bin using esptool. + Attempts multiple methods to detect image layout: flasher_args.json, FLASH_EXTRA_IMAGES, fallback guesses. + """ + firmware_name = os.path.basename(env.subst("$PROGNAME")) + ".bin" + build_dir = pathlib.Path(env.subst("$BUILD_DIR")) + firmware_path = build_dir / firmware_name + flash_size = env.BoardConfig().get("upload.flash_size", "4MB") + chip = env.BoardConfig().get("build.mcu", "esp32") -# Copy over the default sdkconfig. -from os import path + sections = [] + flasher_args_path = build_dir / "flasher_args.json" -if path.exists("./sdkconfig.defaults"): - os.makedirs(".temp", exist_ok=True) - shutil.copy("./sdkconfig.defaults", "./.temp/sdkconfig-esp32-idf") + # 1. Try flasher_args.json + if flasher_args_path.exists(): + try: + with flasher_args_path.open() as f: + flash_data = json.load(f) + for addr, fname in sorted(flash_data["flash_files"].items(), key=lambda kv: int(kv[0], 16)): + file_path = pathlib.Path(fname) + if file_path.exists(): + sections.append((addr, str(file_path))) + else: + print(f"Info: {file_path.name} not found - skipping") + except Exception as e: + print(f"Warning: Failed to parse flasher_args.json - {e}") + # 2. Try FLASH_EXTRA_IMAGES if flasher_args.json failed or was empty + if not sections: + flash_images = env.get("FLASH_EXTRA_IMAGES") + if flash_images: + print("Using FLASH_EXTRA_IMAGES from PlatformIO environment") + # flatten any nested lists + flat = list(itertools.chain.from_iterable( + x if isinstance(x, (list, tuple)) else [x] for x in flash_images + )) + entries = [env.subst(x) for x in flat] + for i in range(0, len(entries) - 1, 2): + addr, fname = entries[i], entries[i + 1] + if isinstance(fname, (list, tuple)): + print(f"Warning: Skipping malformed FLASH_EXTRA_IMAGES entry: {fname}") + continue + file_path = pathlib.Path(str(fname)) + if file_path.exists(): + sections.append((addr, str(file_path))) + else: + print(f"Info: {file_path.name} not found — skipping") -def esp32_create_combined_bin(source, target, env): - verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0"))) - if verbose: - print("Generating combined binary for serial flashing") - app_offset = 0x10000 + # 3. Final fallback: guess standard image locations + if not sections: + print("Fallback: guessing legacy image paths") + guesses = [ + ("0x0", build_dir / "bootloader" / "bootloader.bin"), + ("0x8000", build_dir / "partition_table" / "partition-table.bin"), + ("0xe000", build_dir / "ota_data_initial.bin"), + ("0x10000", firmware_path) + ] + for addr, file_path in guesses: + if file_path.exists(): + sections.append((addr, str(file_path))) + else: + print(f"Info: {file_path.name} not found — skipping") - new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin") - sections = env.subst(env.get("FLASH_EXTRA_IMAGES")) - firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") - chip = env.get("BOARD_MCU") - flash_size = env.BoardConfig().get("upload.flash_size") + # If no valid sections found, skip merge + if not sections: + print("No valid flash sections found — skipping .factory.bin creation.") + return + + output_path = firmware_path.with_suffix(".factory.bin") cmd = [ - "--chip", - chip, + "--chip", chip, "merge_bin", - "-o", - new_file_name, - "--flash_size", - flash_size, + "--flash_size", flash_size, + "--output", str(output_path) ] - if verbose: - print(" Offset | File") - for section in sections: - sect_adr, sect_file = section.split(" ", 1) - if verbose: - print(f" - {sect_adr} | {sect_file}") - cmd += [sect_adr, sect_file] + for addr, file_path in sections: + cmd += [addr, file_path] - cmd += [hex(app_offset), firmware_name] + print(f"Merging binaries into {output_path}") + result = env.Execute( + env.VerboseAction( + f"{env.subst('$PYTHONEXE')} -m esptool " + " ".join(cmd), + "Merging binaries with esptool" + ) + ) - if verbose: - print(f" - {hex(app_offset)} | {firmware_name}") - print() - print(f"Using esptool.py arguments: {' '.join(cmd)}") - print() - - if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: - esptool.main(cmd) + if result == 0: + print(f"Successfully created {output_path}") else: - subprocess.run(["esptool.py", *cmd]) - + print(f"Error: esptool merge_bin failed with code {result}") def esp32_copy_ota_bin(source, target, env): + """ + Copy the main firmware to a .ota.bin file for compatibility with ESPHome OTA tools. + """ firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin") new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.ota.bin") - shutil.copyfile(firmware_name, new_file_name) + print(f"Copied firmware to {new_file_name}") - -# pylint: disable=E0602 -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_copy_ota_bin) # noqa +# Run merge first, then ota copy second +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_factory_bin) +env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_copy_ota_bin) From a3e626757eefcfa80716b972ed4fd0dcc4f467e8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 23 Jul 2025 21:22:54 -0500 Subject: [PATCH 10/20] [helpers] Add "unknown" value handling to ``Deduplicator`` (#9855) --- esphome/core/helpers.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 260479c9e1..f67f13b71f 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -578,21 +578,28 @@ template class CallbackManager { /// Helper class to deduplicate items in a series of values. template class Deduplicator { public: - /// Feeds the next item in the series to the deduplicator and returns whether this is a duplicate. + /// Feeds the next item in the series to the deduplicator and returns false if this is a duplicate. bool next(T value) { - if (this->has_value_) { - if (this->last_value_ == value) - return false; + if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) { + return false; } this->has_value_ = true; + this->value_unknown_ = false; this->last_value_ = value; return true; } - /// Returns whether this deduplicator has processed any items so far. + /// Returns true if the deduplicator's value was previously known. + bool next_unknown() { + bool ret = !this->value_unknown_; + this->value_unknown_ = true; + return ret; + } + /// Returns true if this deduplicator has processed any items. bool has_value() const { return this->has_value_; } protected: bool has_value_{false}; + bool value_unknown_{false}; T last_value_{}; }; From cc187ef2765fb97e406c2e0bf2bfad89441b2854 Mon Sep 17 00:00:00 2001 From: Brandon Harvey <8107750+bharvey88@users.noreply.github.com> Date: Wed, 23 Jul 2025 22:29:39 -0400 Subject: [PATCH 11/20] [ld2450] Set ``accuracy_decimals=0`` as default for "target" entities (#9842) --- esphome/components/ld2450/sensor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/ld2450/sensor.py b/esphome/components/ld2450/sensor.py index 071ce8aa32..d16d9c834d 100644 --- a/esphome/components/ld2450/sensor.py +++ b/esphome/components/ld2450/sensor.py @@ -43,12 +43,15 @@ CONFIG_SCHEMA = cv.Schema( cv.GenerateID(CONF_LD2450_ID): cv.use_id(LD2450Component), cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( icon=ICON_ACCOUNT_GROUP, + accuracy_decimals=0, ), cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( icon=ICON_HUMAN_GREETING_PROXIMITY, + accuracy_decimals=0, ), cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( icon=ICON_ACCOUNT_SWITCH, + accuracy_decimals=0, ), } ) @@ -95,12 +98,15 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend( { cv.Optional(CONF_TARGET_COUNT): sensor.sensor_schema( icon=ICON_MAP_MARKER_ACCOUNT, + accuracy_decimals=0, ), cv.Optional(CONF_STILL_TARGET_COUNT): sensor.sensor_schema( icon=ICON_MAP_MARKER_ACCOUNT, + accuracy_decimals=0, ), cv.Optional(CONF_MOVING_TARGET_COUNT): sensor.sensor_schema( icon=ICON_MAP_MARKER_ACCOUNT, + accuracy_decimals=0, ), } ) From 108e447072b9a8a13917838e0192ce35a3811bd2 Mon Sep 17 00:00:00 2001 From: TJ Horner Date: Wed, 23 Jul 2025 19:51:47 -0700 Subject: [PATCH 12/20] [logger] remove unnecessary call to setTxTimeoutMs (#9854) --- esphome/components/logger/logger_esp32.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index 2fde0f7d49..2ba1efec50 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -119,9 +119,6 @@ void Logger::pre_setup() { #ifdef USE_LOGGER_USB_CDC case UART_SELECTION_USB_CDC: this->hw_serial_ = &Serial; -#if ARDUINO_USB_CDC_ON_BOOT - Serial.setTxTimeoutMs(0); // workaround for 2.0.9 crash when there's no data connection -#endif Serial.begin(this->baud_rate_); break; #endif From 6398bb2fdff277241cb4b058a05b3afbb0bbc449 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 24 Jul 2025 04:14:00 +0100 Subject: [PATCH 13/20] [i2s_audio] Speaker improvements: CPU core agnostic and more accurate timestamps (#9800) Co-authored-by: NP v/d Spek --- esphome/components/i2s_audio/__init__.py | 6 +- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 550 +++++++++--------- .../i2s_audio/speaker/i2s_audio_speaker.h | 53 +- 3 files changed, 302 insertions(+), 307 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 9a2aa0362f..575b914605 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -1,6 +1,6 @@ from esphome import pins import esphome.codegen as cg -from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32, VARIANT_ESP32C3, @@ -258,6 +258,10 @@ async def to_code(config): if use_legacy(): cg.add_define("USE_I2S_LEGACY") + # Helps avoid callbacks being skipped due to processor load + if CORE.using_esp_idf: + add_idf_sdkconfig_option("CONFIG_I2S_ISR_IRAM_SAFE", True) + cg.add(var.set_lrclk_pin(config[CONF_I2S_LRCLK_PIN])) if CONF_I2S_BCLK_PIN in config: cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN])) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 1042a7ebee..6f8c13fe74 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -9,6 +9,7 @@ #endif #include "esphome/components/audio/audio.h" +#include "esphome/components/audio/audio_transfer_buffer.h" #include "esphome/core/application.h" #include "esphome/core/hal.h" @@ -19,72 +20,33 @@ namespace esphome { namespace i2s_audio { -static const uint8_t DMA_BUFFER_DURATION_MS = 15; +static const uint32_t DMA_BUFFER_DURATION_MS = 15; static const size_t DMA_BUFFERS_COUNT = 4; -static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2; - static const size_t TASK_STACK_SIZE = 4096; -static const ssize_t TASK_PRIORITY = 23; +static const ssize_t TASK_PRIORITY = 19; static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1; static const char *const TAG = "i2s_audio.speaker"; enum SpeakerEventGroupBits : uint32_t { - COMMAND_START = (1 << 0), // starts the speaker task + COMMAND_START = (1 << 0), // indicates loop should start speaker task COMMAND_STOP = (1 << 1), // stops the speaker task COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written - STATE_STARTING = (1 << 10), - STATE_RUNNING = (1 << 11), - STATE_STOPPING = (1 << 12), - STATE_STOPPED = (1 << 13), - ERR_TASK_FAILED_TO_START = (1 << 14), - ERR_ESP_INVALID_STATE = (1 << 15), - ERR_ESP_NOT_SUPPORTED = (1 << 16), - ERR_ESP_INVALID_ARG = (1 << 17), - ERR_ESP_INVALID_SIZE = (1 << 18), + + TASK_STARTING = (1 << 10), + TASK_RUNNING = (1 << 11), + TASK_STOPPING = (1 << 12), + TASK_STOPPED = (1 << 13), + ERR_ESP_NO_MEM = (1 << 19), - ERR_ESP_FAIL = (1 << 20), - ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_NOT_SUPPORTED | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | - ERR_ESP_NO_MEM | ERR_ESP_FAIL, + + WARN_DROPPED_EVENT = (1 << 20), + ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits }; -// Translates a SpeakerEventGroupBits ERR_ESP bit to the coressponding esp_err_t -static esp_err_t err_bit_to_esp_err(uint32_t bit) { - switch (bit) { - case SpeakerEventGroupBits::ERR_ESP_INVALID_STATE: - return ESP_ERR_INVALID_STATE; - case SpeakerEventGroupBits::ERR_ESP_INVALID_ARG: - return ESP_ERR_INVALID_ARG; - case SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE: - return ESP_ERR_INVALID_SIZE; - case SpeakerEventGroupBits::ERR_ESP_NO_MEM: - return ESP_ERR_NO_MEM; - case SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED: - return ESP_ERR_NOT_SUPPORTED; - default: - return ESP_FAIL; - } -} - -/// @brief Multiplies the input array of Q15 numbers by a Q15 constant factor -/// -/// Based on `dsps_mulc_s16_ansi` from the esp-dsp library: -/// https://github.com/espressif/esp-dsp/blob/master/modules/math/mulc/fixed/dsps_mulc_s16_ansi.c -/// (accessed on 2024-09-30). -/// @param input Array of Q15 numbers -/// @param output Array of Q15 numbers -/// @param len Length of array -/// @param c Q15 constant factor -static void q15_multiplication(const int16_t *input, int16_t *output, size_t len, int16_t c) { - for (int i = 0; i < len; i++) { - int32_t acc = (int32_t) input[i] * (int32_t) c; - output[i] = (int16_t) (acc >> 15); - } -} - // Lists the Q15 fixed point scaling factor for volume reduction. // Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB. // dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014) @@ -132,51 +94,80 @@ void I2SAudioSpeaker::dump_config() { void I2SAudioSpeaker::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); - if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) { - ESP_LOGD(TAG, "Starting"); + if ((event_group_bits & SpeakerEventGroupBits::COMMAND_START) && (this->state_ == speaker::STATE_STOPPED)) { this->state_ = speaker::STATE_STARTING; - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); } - if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) { + + // Handle the task's state + if (event_group_bits & SpeakerEventGroupBits::TASK_STARTING) { + ESP_LOGD(TAG, "Starting"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STARTING); + } + if (event_group_bits & SpeakerEventGroupBits::TASK_RUNNING) { ESP_LOGD(TAG, "Started"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_RUNNING); this->state_ = speaker::STATE_RUNNING; - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING); - this->status_clear_warning(); - this->status_clear_error(); } - if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) { + if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPING) { ESP_LOGD(TAG, "Stopping"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STOPPING); this->state_ = speaker::STATE_STOPPING; - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING); } - if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) { - if (!this->task_created_) { - ESP_LOGD(TAG, "Stopped"); - this->state_ = speaker::STATE_STOPPED; - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS); - this->speaker_task_handle_ = nullptr; - } + if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPED) { + ESP_LOGD(TAG, "Stopped"); + + vTaskDelete(this->speaker_task_handle_); + this->speaker_task_handle_ = nullptr; + + this->stop_i2s_driver_(); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS); + this->status_clear_error(); + + this->state_ = speaker::STATE_STOPPED; } - if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { - this->status_set_error("Failed to start task"); - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); + // Log any errors encounted by the task + if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NO_MEM) { + ESP_LOGE(TAG, "Not enough memory"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); } - if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { - uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; - ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); - this->status_set_warning(); + // Warn if any playback timestamp events are dropped, which drastically reduces synced playback accuracy + if (event_group_bits & SpeakerEventGroupBits::WARN_DROPPED_EVENT) { + ESP_LOGW(TAG, "Event dropped, synchronized playback accuracy is reduced"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT); } - if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) { - this->status_set_error("Failed to adjust bus to match incoming audio"); - ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u", - this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(), - this->audio_stream_info_.get_bits_per_sample()); - } + // Handle the speaker's state + switch (this->state_) { + case speaker::STATE_STARTING: + if (this->status_has_error()) { + break; + } - xEventGroupClearBits(this->event_group_, ALL_ERR_ESP_BITS); + if (this->start_i2s_driver_(this->audio_stream_info_) != ESP_OK) { + ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second"); + this->status_momentary_error("driver-faiure", 1000); + break; + } + + if (this->speaker_task_handle_ == nullptr) { + xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY, + &this->speaker_task_handle_); + + if (this->speaker_task_handle_ == nullptr) { + ESP_LOGE(TAG, "Task failed to start, retrying in 1 second"); + this->status_momentary_error("task-failure", 1000); + this->stop_i2s_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt + } + } + break; + case speaker::STATE_RUNNING: // Intentional fallthrough + case speaker::STATE_STOPPING: // Intentional fallthrough + case speaker::STATE_STOPPED: + break; + } } void I2SAudioSpeaker::set_volume(float volume) { @@ -227,83 +218,76 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick this->start(); } - if ((this->state_ != speaker::STATE_RUNNING) || (this->audio_ring_buffer_.use_count() != 1)) { + if (this->state_ != speaker::STATE_RUNNING) { // Unable to write data to a running speaker, so delay the max amount of time so it can get ready vTaskDelay(ticks_to_wait); ticks_to_wait = 0; } size_t bytes_written = 0; - if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) { - // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are - // attempting to write to it. - - // Temporarily share ownership of the ring buffer so it won't be deallocated while writing - std::shared_ptr temp_ring_buffer = this->audio_ring_buffer_; - bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait); + if (this->state_ == speaker::STATE_RUNNING) { + std::shared_ptr temp_ring_buffer = this->audio_ring_buffer_.lock(); + if (temp_ring_buffer.use_count() == 2) { + // Only the speaker task and this temp_ring_buffer own the ring buffer, so its safe to write to + bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait); + } } return bytes_written; } bool I2SAudioSpeaker::has_buffered_data() const { - if (this->audio_ring_buffer_ != nullptr) { - return this->audio_ring_buffer_->available() > 0; + if (this->audio_ring_buffer_.use_count() > 0) { + std::shared_ptr temp_ring_buffer = this->audio_ring_buffer_.lock(); + return temp_ring_buffer->available() > 0; } return false; } void I2SAudioSpeaker::speaker_task(void *params) { I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; - this_speaker->task_created_ = true; - uint32_t event_group_bits = - xEventGroupWaitBits(this_speaker->event_group_, - SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP | - SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read - pdTRUE, // Clear the bits on exit - pdFALSE, // Don't wait for all the bits, - portMAX_DELAY); // Block indefinitely until a bit is set - - if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP | SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY)) { - // Received a stop signal before the task was requested to start - this_speaker->delete_task_(0); - } - - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING); - - audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_; + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STARTING); const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT; // Ensure ring buffer duration is at least the duration of all DMA buffers const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this_speaker->buffer_duration_ms_); // The DMA buffers may have more bits per sample, so calculate buffer sizes based in the input audio stream info - const size_t data_buffer_size = audio_stream_info.ms_to_bytes(dma_buffers_duration_ms); - const size_t ring_buffer_size = audio_stream_info.ms_to_bytes(ring_buffer_duration); + const size_t ring_buffer_size = this_speaker->current_stream_info_.ms_to_bytes(ring_buffer_duration); - const size_t single_dma_buffer_input_size = data_buffer_size / DMA_BUFFERS_COUNT; + const uint32_t frames_to_fill_single_dma_buffer = + this_speaker->current_stream_info_.ms_to_frames(DMA_BUFFER_DURATION_MS); + const size_t bytes_to_fill_single_dma_buffer = + this_speaker->current_stream_info_.frames_to_bytes(frames_to_fill_single_dma_buffer); - if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(data_buffer_size, ring_buffer_size))) { - // Failed to allocate buffers - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); - this_speaker->delete_task_(data_buffer_size); + bool successful_setup = false; + std::unique_ptr transfer_buffer = + audio::AudioSourceTransferBuffer::create(bytes_to_fill_single_dma_buffer); + + if (transfer_buffer != nullptr) { + std::shared_ptr temp_ring_buffer = RingBuffer::create(ring_buffer_size); + if (temp_ring_buffer.use_count() == 1) { + transfer_buffer->set_source(temp_ring_buffer); + this_speaker->audio_ring_buffer_ = temp_ring_buffer; + successful_setup = true; + } } - if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) { - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING); - + if (!successful_setup) { + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); + } else { bool stop_gracefully = false; + bool tx_dma_underflow = true; + + uint32_t frames_written = 0; uint32_t last_data_received_time = millis(); - bool tx_dma_underflow = false; - this_speaker->accumulated_frames_written_ = 0; + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_RUNNING); - // Keep looping if paused, there is no timeout configured, or data was received more recently than the configured - // timeout while (this_speaker->pause_state_ || !this_speaker->timeout_.has_value() || (millis() - last_data_received_time) <= this_speaker->timeout_.value()) { - event_group_bits = xEventGroupGetBits(this_speaker->event_group_); + uint32_t event_group_bits = xEventGroupGetBits(this_speaker->event_group_); if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) { xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP); @@ -314,7 +298,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { stop_gracefully = true; } - if (this_speaker->audio_stream_info_ != audio_stream_info) { + if (this_speaker->audio_stream_info_ != this_speaker->current_stream_info_) { // Audio stream info changed, stop the speaker task so it will restart with the proper settings. break; } @@ -326,36 +310,75 @@ void I2SAudioSpeaker::speaker_task(void *params) { } } #else - bool overflow; - while (xQueueReceive(this_speaker->i2s_event_queue_, &overflow, 0)) { - if (overflow) { + int64_t write_timestamp; + while (xQueueReceive(this_speaker->i2s_event_queue_, &write_timestamp, 0)) { + // Receives timing events from the I2S on_sent callback. If actual audio data was sent in this event, it passes + // on the timing info via the audio_output_callback. + uint32_t frames_sent = frames_to_fill_single_dma_buffer; + if (frames_to_fill_single_dma_buffer > frames_written) { tx_dma_underflow = true; + frames_sent = frames_written; + const uint32_t frames_zeroed = frames_to_fill_single_dma_buffer - frames_written; + write_timestamp -= this_speaker->current_stream_info_.frames_to_microseconds(frames_zeroed); + } else { + tx_dma_underflow = false; + } + frames_written -= frames_sent; + if (frames_sent > 0) { + this_speaker->audio_output_callback_(frames_sent, write_timestamp); } } #endif if (this_speaker->pause_state_) { // Pause state is accessed atomically, so thread safe - // Delay so the task can yields, then skip transferring audio data - delay(TASK_DELAY_MS); + // Delay so the task yields, then skip transferring audio data + vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS)); continue; } - size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, data_buffer_size, - pdMS_TO_TICKS(TASK_DELAY_MS)); + // Wait half the duration of the data already written to the DMA buffers for new audio data + // The millisecond helper modifies the frames_written variable, so use the microsecond helper and divide by 1000 + const uint32_t read_delay = + (this_speaker->current_stream_info_.frames_to_microseconds(frames_written) / 1000) / 2; + + uint8_t *new_data = transfer_buffer->get_buffer_end(); // track start of any newly copied bytes + size_t bytes_read = transfer_buffer->transfer_data_from_source(pdMS_TO_TICKS(read_delay)); if (bytes_read > 0) { - if ((audio_stream_info.get_bits_per_sample() == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { - // Scale samples by the volume factor in place - q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_, - bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_); + if (this_speaker->q15_volume_factor_ < INT16_MAX) { + // Apply the software volume adjustment by unpacking the sample into a Q31 fixed-point number, shifting it, + // multiplying by the volume factor, and packing the sample back into the original bytes per sample. + + const size_t bytes_per_sample = this_speaker->current_stream_info_.samples_to_bytes(1); + const uint32_t len = bytes_read / bytes_per_sample; + + // Use Q16 for samples with 1 or 2 bytes: shifted_sample * gain_factor is Q16 * Q15 -> Q31 + int32_t shift = 15; // Q31 -> Q16 + int32_t gain_factor = this_speaker->q15_volume_factor_; // Q15 + + if (bytes_per_sample >= 3) { + // Use Q23 for samples with 3 or 4 bytes: shifted_sample * gain_factor is Q23 * Q8 -> Q31 + + shift = 8; // Q31 -> Q23 + gain_factor >>= 7; // Q15 -> Q8 + } + + for (uint32_t i = 0; i < len; ++i) { + int32_t sample = + audio::unpack_audio_sample_to_q31(&new_data[i * bytes_per_sample], bytes_per_sample); // Q31 + sample >>= shift; + sample *= gain_factor; // Q31 + audio::pack_q31_as_audio_sample(sample, &new_data[i * bytes_per_sample], bytes_per_sample); + } } #ifdef USE_ESP32_VARIANT_ESP32 // For ESP32 8/16 bit mono mode samples need to be switched. - if (audio_stream_info.get_channels() == 1 && audio_stream_info.get_bits_per_sample() <= 16) { + if (this_speaker->current_stream_info_.get_channels() == 1 && + this_speaker->current_stream_info_.get_bits_per_sample() <= 16) { size_t len = bytes_read / sizeof(int16_t); - int16_t *tmp_buf = (int16_t *) this_speaker->data_buffer_; + int16_t *tmp_buf = (int16_t *) new_data; for (int i = 0; i < len; i += 2) { int16_t tmp = tmp_buf[i]; tmp_buf[i] = tmp_buf[i + 1]; @@ -363,62 +386,87 @@ void I2SAudioSpeaker::speaker_task(void *params) { } } #endif - // Write the audio data to a single DMA buffer at a time to reduce latency for the audio duration played - // callback. - const uint32_t batches = (bytes_read + single_dma_buffer_input_size - 1) / single_dma_buffer_input_size; + } - for (uint32_t i = 0; i < batches; ++i) { - size_t bytes_written = 0; - size_t bytes_to_write = std::min(single_dma_buffer_input_size, bytes_read); - -#ifdef USE_I2S_LEGACY - if (audio_stream_info.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) { - i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_ + i * single_dma_buffer_input_size, - bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5)); - } else if (audio_stream_info.get_bits_per_sample() < (uint8_t) this_speaker->bits_per_sample_) { - i2s_write_expand(this_speaker->parent_->get_port(), - this_speaker->data_buffer_ + i * single_dma_buffer_input_size, bytes_to_write, - audio_stream_info.get_bits_per_sample(), this_speaker->bits_per_sample_, &bytes_written, - pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5)); - } -#else - i2s_channel_write(this_speaker->tx_handle_, this_speaker->data_buffer_ + i * single_dma_buffer_input_size, - bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5)); -#endif - - int64_t now = esp_timer_get_time(); - - if (bytes_written != bytes_to_write) { - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); - } - bytes_read -= bytes_written; - - this_speaker->audio_output_callback_(audio_stream_info.bytes_to_frames(bytes_written), - now + dma_buffers_duration_ms * 1000); - - tx_dma_underflow = false; - last_data_received_time = millis(); - } - } else { - // No data received + if (transfer_buffer->available() == 0) { if (stop_gracefully && tx_dma_underflow) { break; } + vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS / 2)); + } else { + size_t bytes_written = 0; +#ifdef USE_I2S_LEGACY + if (this_speaker->current_stream_info_.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) { + i2s_write(this_speaker->parent_->get_port(), transfer_buffer->get_buffer_start(), + transfer_buffer->available(), &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS)); + } else if (this_speaker->current_stream_info_.get_bits_per_sample() < + (uint8_t) this_speaker->bits_per_sample_) { + i2s_write_expand(this_speaker->parent_->get_port(), transfer_buffer->get_buffer_start(), + transfer_buffer->available(), this_speaker->current_stream_info_.get_bits_per_sample(), + this_speaker->bits_per_sample_, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS)); + } +#else + if (tx_dma_underflow) { + // Temporarily disable channel and callback to reset the I2S driver's internal DMA buffer queue so timing + // callbacks are accurate. Preload the data. + i2s_channel_disable(this_speaker->tx_handle_); + const i2s_event_callbacks_t callbacks = { + .on_sent = nullptr, + }; + + i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker); + i2s_channel_preload_data(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(), + transfer_buffer->available(), &bytes_written); + } else { + // Audio is already playing, use regular I2S write to add to the DMA buffers + i2s_channel_write(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(), transfer_buffer->available(), + &bytes_written, DMA_BUFFER_DURATION_MS); + } +#endif + if (bytes_written > 0) { + last_data_received_time = millis(); + frames_written += this_speaker->current_stream_info_.bytes_to_frames(bytes_written); + transfer_buffer->decrease_buffer_length(bytes_written); + if (tx_dma_underflow) { + tx_dma_underflow = false; +#ifndef USE_I2S_LEGACY + // Reset the event queue timestamps + // Enable the on_sent callback to accurately track the timestamps of played audio + // Enable the I2S channel to start sending the preloaded audio + + xQueueReset(this_speaker->i2s_event_queue_); + + const i2s_event_callbacks_t callbacks = { + .on_sent = i2s_on_sent_cb, + }; + i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker); + + i2s_channel_enable(this_speaker->tx_handle_); +#endif + } +#ifdef USE_I2S_LEGACY + // The legacy driver doesn't easily support the callback approach for timestamps, so fall back to a direct but + // less accurate approach. + this_speaker->audio_output_callback_(this_speaker->current_stream_info_.bytes_to_frames(bytes_written), + esp_timer_get_time() + dma_buffers_duration_ms * 1000); +#endif + } } } - - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); -#ifdef USE_I2S_LEGACY - i2s_driver_uninstall(this_speaker->parent_->get_port()); -#else - i2s_channel_disable(this_speaker->tx_handle_); - i2s_del_channel(this_speaker->tx_handle_); -#endif - - this_speaker->parent_->unlock(); } - this_speaker->delete_task_(data_buffer_size); + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPING); + + if (transfer_buffer != nullptr) { + transfer_buffer.reset(); + } + + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPED); + + while (true) { + // Continuously delay until the loop method deletes the task + vTaskDelay(pdMS_TO_TICKS(10)); + } } void I2SAudioSpeaker::start() { @@ -427,16 +475,7 @@ void I2SAudioSpeaker::start() { if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; - if (!this->task_created_ && (this->speaker_task_handle_ == nullptr)) { - xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY, - &this->speaker_task_handle_); - - if (this->speaker_task_handle_ != nullptr) { - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); - } else { - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); - } - } + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); } void I2SAudioSpeaker::stop() { this->stop_(false); } @@ -456,61 +495,16 @@ void I2SAudioSpeaker::stop_(bool wait_on_empty) { } } -bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) { - switch (err) { - case ESP_OK: - return false; - case ESP_ERR_INVALID_STATE: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_STATE); - return true; - case ESP_ERR_INVALID_ARG: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_ARG); - return true; - case ESP_ERR_INVALID_SIZE: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); - return true; - case ESP_ERR_NO_MEM: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); - return true; - case ESP_ERR_NOT_SUPPORTED: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED); - return true; - default: - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL); - return true; - } -} - -esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) { - if (this->data_buffer_ == nullptr) { - // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus - RAMAllocator allocator; - this->data_buffer_ = allocator.allocate(data_buffer_size); - } - - if (this->data_buffer_ == nullptr) { - return ESP_ERR_NO_MEM; - } - - if (this->audio_ring_buffer_.use_count() == 0) { - // Allocate ring buffer. Uses a shared_ptr to ensure it isn't improperly deallocated. - this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size); - } - - if (this->audio_ring_buffer_ == nullptr) { - return ESP_ERR_NO_MEM; - } - - return ESP_OK; -} - esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) { + this->current_stream_info_ = audio_stream_info; // store the stream info settings the driver will use + #ifdef USE_I2S_LEGACY if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT #else if ((this->i2s_role_ & I2S_ROLE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT #endif // Can't reconfigure I2S bus, so the sample rate must match the configured value + ESP_LOGE(TAG, "Audio stream settings are not compatible with this I2S configuration"); return ESP_ERR_NOT_SUPPORTED; } @@ -521,10 +515,12 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea (i2s_slot_bit_width_t) audio_stream_info.get_bits_per_sample() > this->slot_bit_width_) { #endif // Currently can't handle the case when the incoming audio has more bits per sample than the configured value + ESP_LOGE(TAG, "Audio streams with more bits per sample than the I2S speaker's configuration is not supported"); return ESP_ERR_NOT_SUPPORTED; } if (!this->parent_->try_lock()) { + ESP_LOGE(TAG, "Parent I2S bus not free"); return ESP_ERR_INVALID_STATE; } @@ -575,6 +571,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea esp_err_t err = i2s_driver_install(this->parent_->get_port(), &config, I2S_EVENT_QUEUE_COUNT, &this->i2s_event_queue_); if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to install I2S legacy driver"); // Failed to install the driver, so unlock the I2S port this->parent_->unlock(); return err; @@ -595,6 +592,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea if (err != ESP_OK) { // Failed to set the data out pin, so uninstall the driver and unlock the I2S port + ESP_LOGE(TAG, "Failed to set the data out pin"); i2s_driver_uninstall(this->parent_->get_port()); this->parent_->unlock(); } @@ -605,10 +603,12 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea .dma_desc_num = DMA_BUFFERS_COUNT, .dma_frame_num = dma_buffer_length, .auto_clear = true, + .intr_priority = 3, }; /* Allocate a new TX channel and get the handle of this channel */ esp_err_t err = i2s_new_channel(&chan_cfg, &this->tx_handle_, NULL); if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to allocate new I2S channel"); this->parent_->unlock(); return err; } @@ -652,7 +652,11 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea // per sample causes the audio to play too fast. Setting the ws_width to the configured slot bit width seems to // make it play at the correct speed while sending more bits per slot. if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) { - std_slot_cfg.ws_width = static_cast(this->slot_bit_width_); + uint32_t configured_bit_width = static_cast(this->slot_bit_width_); + std_slot_cfg.ws_width = configured_bit_width; + if (configured_bit_width > 16) { + std_slot_cfg.msb_right = false; + } } #else std_slot_cfg.slot_bit_width = this->slot_bit_width_; @@ -670,54 +674,56 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea err = i2s_channel_init_std_mode(this->tx_handle_, &std_cfg); if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize channel"); i2s_del_channel(this->tx_handle_); + this->tx_handle_ = nullptr; this->parent_->unlock(); return err; } if (this->i2s_event_queue_ == nullptr) { - this->i2s_event_queue_ = xQueueCreate(1, sizeof(bool)); + this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(int64_t)); } - const i2s_event_callbacks_t callbacks = { - .on_send_q_ovf = i2s_overflow_cb, - }; - i2s_channel_register_event_callback(this->tx_handle_, &callbacks, this); - - /* Before reading data, start the TX channel first */ i2s_channel_enable(this->tx_handle_); - if (err != ESP_OK) { - i2s_del_channel(this->tx_handle_); - this->parent_->unlock(); - } #endif return err; } -void I2SAudioSpeaker::delete_task_(size_t buffer_size) { - this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr +#ifndef USE_I2S_LEGACY +bool IRAM_ATTR I2SAudioSpeaker::i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) { + int64_t now = esp_timer_get_time(); - if (this->data_buffer_ != nullptr) { - RAMAllocator allocator; - allocator.deallocate(this->data_buffer_, buffer_size); - this->data_buffer_ = nullptr; + BaseType_t need_yield1 = pdFALSE; + BaseType_t need_yield2 = pdFALSE; + BaseType_t need_yield3 = pdFALSE; + + I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx; + + if (xQueueIsQueueFullFromISR(this_speaker->i2s_event_queue_)) { + // Queue is full, so discard the oldest event and set the warning flag to inform the user + int64_t dummy; + xQueueReceiveFromISR(this_speaker->i2s_event_queue_, &dummy, &need_yield1); + xEventGroupSetBitsFromISR(this_speaker->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT, &need_yield2); } - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED); + xQueueSendToBackFromISR(this_speaker->i2s_event_queue_, &now, &need_yield3); - this->task_created_ = false; - vTaskDelete(nullptr); -} - -#ifndef USE_I2S_LEGACY -bool IRAM_ATTR I2SAudioSpeaker::i2s_overflow_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) { - I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx; - bool overflow = true; - xQueueOverwrite(this_speaker->i2s_event_queue_, &overflow); - return false; + return need_yield1 | need_yield2 | need_yield3; } #endif +void I2SAudioSpeaker::stop_i2s_driver_() { +#ifdef USE_I2S_LEGACY + i2s_driver_uninstall(this->parent_->get_port()); +#else + i2s_channel_disable(this->tx_handle_); + i2s_del_channel(this->tx_handle_); + this->tx_handle_ = nullptr; +#endif + this->parent_->unlock(); +} + } // namespace i2s_audio } // namespace esphome diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index eb2a0ae756..1d03a4c495 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -72,70 +72,57 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp protected: /// @brief Function for the FreeRTOS task handling audio output. - /// After receiving the COMMAND_START signal, allocates space for the buffers, starts the I2S driver, and reads - /// audio from the ring buffer and writes audio to the I2S port. Stops immmiately after receiving the COMMAND_STOP - /// signal and stops only after the ring buffer is empty after receiving the COMMAND_STOP_GRACEFULLY signal. Stops if - /// the ring buffer hasn't read data for more than timeout_ milliseconds. When stopping, it deallocates the buffers, - /// stops the I2S driver, unlocks the I2S port, and deletes the task. It communicates the state and any errors via - /// event_group_. + /// Allocates space for the buffers, reads audio from the ring buffer and writes audio to the I2S port. Stops + /// immmiately after receiving the COMMAND_STOP signal and stops only after the ring buffer is empty after receiving + /// the COMMAND_STOP_GRACEFULLY signal. Stops if the ring buffer hasn't read data for more than timeout_ milliseconds. + /// When stopping, it deallocates the buffers. It communicates its state and any errors via ``event_group_``. /// @param params I2SAudioSpeaker component static void speaker_task(void *params); - /// @brief Sends a stop command to the speaker task via event_group_. + /// @brief Sends a stop command to the speaker task via ``event_group_``. /// @param wait_on_empty If false, sends the COMMAND_STOP signal. If true, sends the COMMAND_STOP_GRACEFULLY signal. void stop_(bool wait_on_empty); - /// @brief Sets the corresponding ERR_ESP event group bits. - /// @param err esp_err_t error code. - /// @return True if an ERR_ESP bit is set and false if err == ESP_OK - bool send_esp_err_to_event_group_(esp_err_t err); - #ifndef USE_I2S_LEGACY - static bool i2s_overflow_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx); + /// @brief Callback function used to send playback timestamps the to the speaker task. + /// @param handle (i2s_chan_handle_t) + /// @param event (i2s_event_data_t) + /// @param user_ctx (void*) User context pointer that the callback accesses + /// @return True if a higher priority task was interrupted + static bool i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx); #endif - /// @brief Allocates the data buffer and ring buffer - /// @param data_buffer_size Number of bytes to allocate for the data buffer. - /// @param ring_buffer_size Number of bytes to allocate for the ring buffer. - /// @return ESP_ERR_NO_MEM if either buffer fails to allocate - /// ESP_OK if successful - esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size); - /// @brief Starts the ESP32 I2S driver. /// Attempts to lock the I2S port, starts the I2S driver using the passed in stream information, and sets the data out - /// pin. If it fails, it will unlock the I2S port and uninstall the driver, if necessary. + /// pin. If it fails, it will unlock the I2S port and uninstalls the driver, if necessary. /// @param audio_stream_info Stream information for the I2S driver. /// @return ESP_ERR_NOT_ALLOWED if the I2S port can't play the incoming audio stream. /// ESP_ERR_INVALID_STATE if the I2S port is already locked. - /// ESP_ERR_INVALID_ARG if nstalling the driver or setting the data outpin fails due to a parameter error. + /// ESP_ERR_INVALID_ARG if installing the driver or setting the data outpin fails due to a parameter error. /// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error. - /// ESP_FAIL if setting the data out pin fails due to an IO error ESP_OK if successful + /// ESP_FAIL if setting the data out pin fails due to an IO error + /// ESP_OK if successful esp_err_t start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info); - /// @brief Deletes the speaker's task. - /// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by - /// the speaker_task itself. - /// @param buffer_size The allocated size of the data_buffer_. - void delete_task_(size_t buffer_size); + /// @brief Stops the I2S driver and unlocks the I2S port + void stop_i2s_driver_(); TaskHandle_t speaker_task_handle_{nullptr}; EventGroupHandle_t event_group_{nullptr}; QueueHandle_t i2s_event_queue_; - uint8_t *data_buffer_; - std::shared_ptr audio_ring_buffer_; + std::weak_ptr audio_ring_buffer_; uint32_t buffer_duration_ms_; optional timeout_; - bool task_created_{false}; bool pause_state_{false}; int16_t q15_volume_factor_{INT16_MAX}; - size_t bytes_written_{0}; + audio::AudioStreamInfo current_stream_info_; // The currently loaded driver's stream info #ifdef USE_I2S_LEGACY #if SOC_I2S_SUPPORTS_DAC @@ -148,8 +135,6 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp std::string i2s_comm_fmt_; i2s_chan_handle_t tx_handle_; #endif - - uint32_t accumulated_frames_written_{0}; }; } // namespace i2s_audio From 15ba2326ad188a270f58014eee6525bbe9af16de Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:15:32 -1000 Subject: [PATCH 14/20] [esp32] Fix threading model for single-core variants (S2, C3, C6, H2) (#9851) --- esphome/components/esp32/__init__.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 587d75b64b..e24815741a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -98,6 +98,16 @@ ARDUINO_ALLOWED_VARIANTS = [ VARIANT_ESP32S3, ] +# Single-core ESP32 variants +SINGLE_CORE_VARIANTS = frozenset( + [ + VARIANT_ESP32S2, + VARIANT_ESP32C3, + VARIANT_ESP32C6, + VARIANT_ESP32H2, + ] +) + def get_cpu_frequencies(*frequencies): return [str(x) + "MHZ" for x in frequencies] @@ -714,7 +724,11 @@ async def to_code(config): cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}") cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]]) - cg.add_define(CoreModel.MULTI_ATOMICS) + # Set threading model based on core count + if config[CONF_VARIANT] in SINGLE_CORE_VARIANTS: + cg.add_define(CoreModel.SINGLE) + else: + cg.add_define(CoreModel.MULTI_ATOMICS) cg.add_platformio_option("lib_ldf_mode", "off") cg.add_platformio_option("lib_compat_mode", "strict") From 04d9698681efcd1f25836d4333ed21382b12f916 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:16:54 -1000 Subject: [PATCH 15/20] [api] Replace magic numbers with MESSAGE_TYPE constants in protobuf switch cases (#9776) --- esphome/components/api/api_pb2_service.cpp | 110 ++++++++++----------- script/api_protobuf/api_protobuf.py | 9 +- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 498c396ae3..4ac2f75fb0 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -16,7 +16,7 @@ void APIServerConnectionBase::log_send_message_(const char *name, const std::str void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { - case 1: { + case HelloRequest::MESSAGE_TYPE: { HelloRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -25,7 +25,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_hello_request(msg); break; } - case 3: { + case ConnectRequest::MESSAGE_TYPE: { ConnectRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -34,7 +34,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_connect_request(msg); break; } - case 5: { + case DisconnectRequest::MESSAGE_TYPE: { DisconnectRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -43,7 +43,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_disconnect_request(msg); break; } - case 6: { + case DisconnectResponse::MESSAGE_TYPE: { DisconnectResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -52,7 +52,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_disconnect_response(msg); break; } - case 7: { + case PingRequest::MESSAGE_TYPE: { PingRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -61,7 +61,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_ping_request(msg); break; } - case 8: { + case PingResponse::MESSAGE_TYPE: { PingResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -70,7 +70,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_ping_response(msg); break; } - case 9: { + case DeviceInfoRequest::MESSAGE_TYPE: { DeviceInfoRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -79,7 +79,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_device_info_request(msg); break; } - case 11: { + case ListEntitiesRequest::MESSAGE_TYPE: { ListEntitiesRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -88,7 +88,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_list_entities_request(msg); break; } - case 20: { + case SubscribeStatesRequest::MESSAGE_TYPE: { SubscribeStatesRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -97,7 +97,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_subscribe_states_request(msg); break; } - case 28: { + case SubscribeLogsRequest::MESSAGE_TYPE: { SubscribeLogsRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -107,7 +107,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, break; } #ifdef USE_COVER - case 30: { + case CoverCommandRequest::MESSAGE_TYPE: { CoverCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -118,7 +118,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_FAN - case 31: { + case FanCommandRequest::MESSAGE_TYPE: { FanCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -129,7 +129,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_LIGHT - case 32: { + case LightCommandRequest::MESSAGE_TYPE: { LightCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -140,7 +140,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_SWITCH - case 33: { + case SwitchCommandRequest::MESSAGE_TYPE: { SwitchCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -150,7 +150,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, break; } #endif - case 34: { + case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: { SubscribeHomeassistantServicesRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -159,7 +159,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_subscribe_homeassistant_services_request(msg); break; } - case 36: { + case GetTimeRequest::MESSAGE_TYPE: { GetTimeRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -168,7 +168,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_get_time_request(msg); break; } - case 37: { + case GetTimeResponse::MESSAGE_TYPE: { GetTimeResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -177,7 +177,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_get_time_response(msg); break; } - case 38: { + case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: { SubscribeHomeAssistantStatesRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -186,7 +186,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_subscribe_home_assistant_states_request(msg); break; } - case 40: { + case HomeAssistantStateResponse::MESSAGE_TYPE: { HomeAssistantStateResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -196,7 +196,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, break; } #ifdef USE_API_SERVICES - case 42: { + case ExecuteServiceRequest::MESSAGE_TYPE: { ExecuteServiceRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -207,7 +207,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_CAMERA - case 45: { + case CameraImageRequest::MESSAGE_TYPE: { CameraImageRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -218,7 +218,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_CLIMATE - case 48: { + case ClimateCommandRequest::MESSAGE_TYPE: { ClimateCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -229,7 +229,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_NUMBER - case 51: { + case NumberCommandRequest::MESSAGE_TYPE: { NumberCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -240,7 +240,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_SELECT - case 54: { + case SelectCommandRequest::MESSAGE_TYPE: { SelectCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -251,7 +251,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_SIREN - case 57: { + case SirenCommandRequest::MESSAGE_TYPE: { SirenCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -262,7 +262,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_LOCK - case 60: { + case LockCommandRequest::MESSAGE_TYPE: { LockCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -273,7 +273,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BUTTON - case 62: { + case ButtonCommandRequest::MESSAGE_TYPE: { ButtonCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -284,7 +284,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_MEDIA_PLAYER - case 65: { + case MediaPlayerCommandRequest::MESSAGE_TYPE: { MediaPlayerCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -295,7 +295,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 66: { + case SubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: { SubscribeBluetoothLEAdvertisementsRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -306,7 +306,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 68: { + case BluetoothDeviceRequest::MESSAGE_TYPE: { BluetoothDeviceRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -317,7 +317,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 70: { + case BluetoothGATTGetServicesRequest::MESSAGE_TYPE: { BluetoothGATTGetServicesRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -328,7 +328,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 73: { + case BluetoothGATTReadRequest::MESSAGE_TYPE: { BluetoothGATTReadRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -339,7 +339,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 75: { + case BluetoothGATTWriteRequest::MESSAGE_TYPE: { BluetoothGATTWriteRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -350,7 +350,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 76: { + case BluetoothGATTReadDescriptorRequest::MESSAGE_TYPE: { BluetoothGATTReadDescriptorRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -361,7 +361,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 77: { + case BluetoothGATTWriteDescriptorRequest::MESSAGE_TYPE: { BluetoothGATTWriteDescriptorRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -372,7 +372,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 78: { + case BluetoothGATTNotifyRequest::MESSAGE_TYPE: { BluetoothGATTNotifyRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -383,7 +383,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 80: { + case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: { SubscribeBluetoothConnectionsFreeRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -394,7 +394,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 87: { + case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: { UnsubscribeBluetoothLEAdvertisementsRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -405,7 +405,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 89: { + case SubscribeVoiceAssistantRequest::MESSAGE_TYPE: { SubscribeVoiceAssistantRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -416,7 +416,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 91: { + case VoiceAssistantResponse::MESSAGE_TYPE: { VoiceAssistantResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -427,7 +427,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 92: { + case VoiceAssistantEventResponse::MESSAGE_TYPE: { VoiceAssistantEventResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -438,7 +438,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_ALARM_CONTROL_PANEL - case 96: { + case AlarmControlPanelCommandRequest::MESSAGE_TYPE: { AlarmControlPanelCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -449,7 +449,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_TEXT - case 99: { + case TextCommandRequest::MESSAGE_TYPE: { TextCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -460,7 +460,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_DATETIME_DATE - case 102: { + case DateCommandRequest::MESSAGE_TYPE: { DateCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -471,7 +471,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_DATETIME_TIME - case 105: { + case TimeCommandRequest::MESSAGE_TYPE: { TimeCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -482,7 +482,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 106: { + case VoiceAssistantAudio::MESSAGE_TYPE: { VoiceAssistantAudio msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -493,7 +493,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VALVE - case 111: { + case ValveCommandRequest::MESSAGE_TYPE: { ValveCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -504,7 +504,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_DATETIME_DATETIME - case 114: { + case DateTimeCommandRequest::MESSAGE_TYPE: { DateTimeCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -515,7 +515,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 115: { + case VoiceAssistantTimerEventResponse::MESSAGE_TYPE: { VoiceAssistantTimerEventResponse msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -526,7 +526,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_UPDATE - case 118: { + case UpdateCommandRequest::MESSAGE_TYPE: { UpdateCommandRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -537,7 +537,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 119: { + case VoiceAssistantAnnounceRequest::MESSAGE_TYPE: { VoiceAssistantAnnounceRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -548,7 +548,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 121: { + case VoiceAssistantConfigurationRequest::MESSAGE_TYPE: { VoiceAssistantConfigurationRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -559,7 +559,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_VOICE_ASSISTANT - case 123: { + case VoiceAssistantSetConfiguration::MESSAGE_TYPE: { VoiceAssistantSetConfiguration msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -570,7 +570,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_API_NOISE - case 124: { + case NoiseEncryptionSetKeyRequest::MESSAGE_TYPE: { NoiseEncryptionSetKeyRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP @@ -581,7 +581,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, } #endif #ifdef USE_BLUETOOTH_PROXY - case 127: { + case BluetoothScannerSetModeRequest::MESSAGE_TYPE: { BluetoothScannerSetModeRequest msg; msg.decode(msg_data, msg_size); #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 766a84c9fd..24ee1aaa47 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1934,8 +1934,8 @@ def build_service_message_type( case += "#endif\n" case += f"this->{func}(msg);\n" case += "break;" - # Store the ifdef with the case for later use - RECEIVE_CASES[id_] = (case, ifdef) + # Store the message name and ifdef with the case for later use + RECEIVE_CASES[id_] = (case, ifdef, mt.name) # Only close ifdef if we opened it if ifdef is not None: @@ -2200,10 +2200,11 @@ static const char *const TAG = "api.service"; hpp += " void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n" out = f"void {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n" out += " switch (msg_type) {\n" - for i, (case, ifdef) in cases: + for i, (case, ifdef, message_name) in cases: if ifdef is not None: out += f"#ifdef {ifdef}\n" - c = f" case {i}: {{\n" + + c = f" case {message_name}::MESSAGE_TYPE: {{\n" c += indent(case, " ") + "\n" c += " }" out += c + "\n" From f863189f96c4b048efbf56fd8e326fecd6340624 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:18:01 -1000 Subject: [PATCH 16/20] [api] Simplify generated authentication check code (#9806) --- esphome/components/api/api_pb2_service.cpp | 30 ++++++++-------------- script/api_protobuf/api_protobuf.py | 19 ++++++-------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 4ac2f75fb0..d8ff4fcd24 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -617,10 +617,8 @@ void APIServerConnection::on_ping_request(const PingRequest &msg) { } } void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) { - if (this->check_connection_setup_()) { - if (!this->send_device_info_response(msg)) { - this->on_fatal_error(); - } + if (this->check_connection_setup_() && !this->send_device_info_response(msg)) { + this->on_fatal_error(); } } void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { @@ -650,10 +648,8 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc } } void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { - if (this->check_connection_setup_()) { - if (!this->send_get_time_response(msg)) { - this->on_fatal_error(); - } + if (this->check_connection_setup_() && !this->send_get_time_response(msg)) { + this->on_fatal_error(); } } #ifdef USE_API_SERVICES @@ -665,10 +661,8 @@ void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest #endif #ifdef USE_API_NOISE void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) { - if (this->check_authenticated_()) { - if (!this->send_noise_encryption_set_key_response(msg)) { - this->on_fatal_error(); - } + if (this->check_authenticated_() && !this->send_noise_encryption_set_key_response(msg)) { + this->on_fatal_error(); } } #endif @@ -858,10 +852,8 @@ void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNo #ifdef USE_BLUETOOTH_PROXY void APIServerConnection::on_subscribe_bluetooth_connections_free_request( const SubscribeBluetoothConnectionsFreeRequest &msg) { - if (this->check_authenticated_()) { - if (!this->send_subscribe_bluetooth_connections_free_response(msg)) { - this->on_fatal_error(); - } + if (this->check_authenticated_() && !this->send_subscribe_bluetooth_connections_free_response(msg)) { + this->on_fatal_error(); } } #endif @@ -889,10 +881,8 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo #endif #ifdef USE_VOICE_ASSISTANT void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) { - if (this->check_authenticated_()) { - if (!this->send_voice_assistant_get_configuration_response(msg)) { - this->on_fatal_error(); - } + if (this->check_authenticated_() && !this->send_voice_assistant_get_configuration_response(msg)) { + this->on_fatal_error(); } } #endif diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 24ee1aaa47..fb5db70ae8 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2261,19 +2261,16 @@ static const char *const TAG = "api.service"; else: check_func = "this->check_connection_setup_()" - body = f"if ({check_func}) {{\n" - - # Add the actual handler code, indented - handler_body = "" if is_void: - handler_body = f"this->{func}(msg);\n" + # For void methods, just wrap with auth check + body = f"if ({check_func}) {{\n" + body += f" this->{func}(msg);\n" + body += "}\n" else: - handler_body = f"if (!this->send_{func}_response(msg)) {{\n" - handler_body += " this->on_fatal_error();\n" - handler_body += "}\n" - - body += indent(handler_body) + "\n" - body += "}\n" + # For non-void methods, combine auth check and send response check + body = f"if ({check_func} && !this->send_{func}_response(msg)) {{\n" + body += " this->on_fatal_error();\n" + body += "}\n" else: # No auth check needed, just call the handler body = "" From 4a27b34685ff6792e352355d2cf265849e2b7528 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:19:58 -1000 Subject: [PATCH 17/20] [api] Reduce code duplication in protobuf dump methods with helper functions (#9809) --- esphome/components/api/api_pb2_dump.cpp | 4042 +++++------------------ script/api_protobuf/api_protobuf.py | 195 +- 2 files changed, 1014 insertions(+), 3223 deletions(-) diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index 4e44bff11e..a2e69255e1 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -19,6 +19,82 @@ static inline void append_quoted_string(std::string &out, const StringRef &ref) out.append("'"); } +// Common helpers for dump_field functions +static inline void append_field_prefix(std::string &out, const char *field_name, int indent) { + out.append(indent, ' ').append(field_name).append(": "); +} + +static inline void append_with_newline(std::string &out, const char *str) { + out.append(str); + out.append("\n"); +} + +// RAII helper for message dump formatting +class MessageDumpHelper { + public: + MessageDumpHelper(std::string &out, const char *message_name) : out_(out) { + out_.append(message_name); + out_.append(" {\n"); + } + ~MessageDumpHelper() { out_.append(" }"); } + + private: + std::string &out_; +}; + +// Helper functions to reduce code duplication in dump methods +static void dump_field(std::string &out, const char *field_name, int32_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%" PRId32, value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, uint32_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%" PRIu32, value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, float value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%g", value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, uint64_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%llu", value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, bool value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append(YESNO(value)); + out.append("\n"); +} + +static void dump_field(std::string &out, const char *field_name, const std::string &value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append("'").append(value).append("'"); + out.append("\n"); +} + +static void dump_field(std::string &out, const char *field_name, StringRef value, int indent = 2) { + append_field_prefix(out, field_name, indent); + append_quoted_string(out, value); + out.append("\n"); +} + +template static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append(proto_enum_to_string(value)); + out.append("\n"); +} + template<> const char *proto_enum_to_string(enums::EntityCategory value) { switch (value) { case enums::ENTITY_CATEGORY_NONE: @@ -558,61 +634,20 @@ template<> const char *proto_enum_to_string(enums::UpdateC #endif void HelloRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("HelloRequest {\n"); - out.append(" client_info: "); - out.append("'").append(this->client_info).append("'"); - out.append("\n"); - - out.append(" api_version_major: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->api_version_major); - out.append(buffer); - out.append("\n"); - - out.append(" api_version_minor: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->api_version_minor); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "HelloRequest"); + dump_field(out, "client_info", this->client_info); + dump_field(out, "api_version_major", this->api_version_major); + dump_field(out, "api_version_minor", this->api_version_minor); } void HelloResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("HelloResponse {\n"); - out.append(" api_version_major: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->api_version_major); - out.append(buffer); - out.append("\n"); - - out.append(" api_version_minor: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->api_version_minor); - out.append(buffer); - out.append("\n"); - - out.append(" server_info: "); - append_quoted_string(out, this->server_info_ref_); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - out.append("}"); -} -void ConnectRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ConnectRequest {\n"); - out.append(" password: "); - out.append("'").append(this->password).append("'"); - out.append("\n"); - out.append("}"); -} -void ConnectResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ConnectResponse {\n"); - out.append(" invalid_password: "); - out.append(YESNO(this->invalid_password)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "HelloResponse"); + dump_field(out, "api_version_major", this->api_version_major); + dump_field(out, "api_version_minor", this->api_version_minor); + dump_field(out, "server_info", this->server_info_ref_); + dump_field(out, "name", this->name_ref_); } +void ConnectRequest::dump_to(std::string &out) const { dump_field(out, "password", this->password); } +void ConnectResponse::dump_to(std::string &out) const { dump_field(out, "invalid_password", this->invalid_password); } void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); } void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); } void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); } @@ -620,132 +655,57 @@ void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {} void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } #ifdef USE_AREAS void AreaInfo::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("AreaInfo {\n"); - out.append(" area_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->area_id); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "AreaInfo"); + dump_field(out, "area_id", this->area_id); + dump_field(out, "name", this->name_ref_); } #endif #ifdef USE_DEVICES void DeviceInfo::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DeviceInfo {\n"); - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" area_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->area_id); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "DeviceInfo"); + dump_field(out, "device_id", this->device_id); + dump_field(out, "name", this->name_ref_); + dump_field(out, "area_id", this->area_id); } #endif void DeviceInfoResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DeviceInfoResponse {\n"); + MessageDumpHelper helper(out, "DeviceInfoResponse"); #ifdef USE_API_PASSWORD - out.append(" uses_password: "); - out.append(YESNO(this->uses_password)); - out.append("\n"); - + dump_field(out, "uses_password", this->uses_password); #endif - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" mac_address: "); - append_quoted_string(out, this->mac_address_ref_); - out.append("\n"); - - out.append(" esphome_version: "); - append_quoted_string(out, this->esphome_version_ref_); - out.append("\n"); - - out.append(" compilation_time: "); - append_quoted_string(out, this->compilation_time_ref_); - out.append("\n"); - - out.append(" model: "); - append_quoted_string(out, this->model_ref_); - out.append("\n"); - + dump_field(out, "name", this->name_ref_); + dump_field(out, "mac_address", this->mac_address_ref_); + dump_field(out, "esphome_version", this->esphome_version_ref_); + dump_field(out, "compilation_time", this->compilation_time_ref_); + dump_field(out, "model", this->model_ref_); #ifdef USE_DEEP_SLEEP - out.append(" has_deep_sleep: "); - out.append(YESNO(this->has_deep_sleep)); - out.append("\n"); - + dump_field(out, "has_deep_sleep", this->has_deep_sleep); #endif #ifdef ESPHOME_PROJECT_NAME - out.append(" project_name: "); - append_quoted_string(out, this->project_name_ref_); - out.append("\n"); - + dump_field(out, "project_name", this->project_name_ref_); #endif #ifdef ESPHOME_PROJECT_NAME - out.append(" project_version: "); - append_quoted_string(out, this->project_version_ref_); - out.append("\n"); - + dump_field(out, "project_version", this->project_version_ref_); #endif #ifdef USE_WEBSERVER - out.append(" webserver_port: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->webserver_port); - out.append(buffer); - out.append("\n"); - + dump_field(out, "webserver_port", this->webserver_port); #endif #ifdef USE_BLUETOOTH_PROXY - out.append(" bluetooth_proxy_feature_flags: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->bluetooth_proxy_feature_flags); - out.append(buffer); - out.append("\n"); - + dump_field(out, "bluetooth_proxy_feature_flags", this->bluetooth_proxy_feature_flags); #endif - out.append(" manufacturer: "); - append_quoted_string(out, this->manufacturer_ref_); - out.append("\n"); - - out.append(" friendly_name: "); - append_quoted_string(out, this->friendly_name_ref_); - out.append("\n"); - + dump_field(out, "manufacturer", this->manufacturer_ref_); + dump_field(out, "friendly_name", this->friendly_name_ref_); #ifdef USE_VOICE_ASSISTANT - out.append(" voice_assistant_feature_flags: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->voice_assistant_feature_flags); - out.append(buffer); - out.append("\n"); - + dump_field(out, "voice_assistant_feature_flags", this->voice_assistant_feature_flags); #endif #ifdef USE_AREAS - out.append(" suggested_area: "); - append_quoted_string(out, this->suggested_area_ref_); - out.append("\n"); - + dump_field(out, "suggested_area", this->suggested_area_ref_); #endif #ifdef USE_BLUETOOTH_PROXY - out.append(" bluetooth_mac_address: "); - append_quoted_string(out, this->bluetooth_mac_address_ref_); - out.append("\n"); - + dump_field(out, "bluetooth_mac_address", this->bluetooth_mac_address_ref_); #endif #ifdef USE_API_NOISE - out.append(" api_encryption_supported: "); - out.append(YESNO(this->api_encryption_supported)); - out.append("\n"); - + dump_field(out, "api_encryption_supported", this->api_encryption_supported); #endif #ifdef USE_DEVICES for (const auto &it : this->devices) { @@ -753,7 +713,6 @@ void DeviceInfoResponse::dump_to(std::string &out) const { it.dump_to(out); out.append("\n"); } - #endif #ifdef USE_AREAS for (const auto &it : this->areas) { @@ -761,2736 +720,1008 @@ void DeviceInfoResponse::dump_to(std::string &out) const { it.dump_to(out); out.append("\n"); } - #endif #ifdef USE_AREAS out.append(" area: "); this->area.dump_to(out); out.append("\n"); - #endif - out.append("}"); } void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); } void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); } void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); } #ifdef USE_BINARY_SENSOR void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesBinarySensorResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - - out.append(" is_status_binary_sensor: "); - out.append(YESNO(this->is_status_binary_sensor)); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesBinarySensorResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); + dump_field(out, "device_class", this->device_class_ref_); + dump_field(out, "is_status_binary_sensor", this->is_status_binary_sensor); + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void BinarySensorStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BinarySensorStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "BinarySensorStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_COVER void ListEntitiesCoverResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesCoverResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" assumed_state: "); - out.append(YESNO(this->assumed_state)); - out.append("\n"); - - out.append(" supports_position: "); - out.append(YESNO(this->supports_position)); - out.append("\n"); - - out.append(" supports_tilt: "); - out.append(YESNO(this->supports_tilt)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesCoverResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); + dump_field(out, "assumed_state", this->assumed_state); + dump_field(out, "supports_position", this->supports_position); + dump_field(out, "supports_tilt", this->supports_tilt); + dump_field(out, "device_class", this->device_class_ref_); + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" supports_stop: "); - out.append(YESNO(this->supports_stop)); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "supports_stop", this->supports_stop); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void CoverStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("CoverStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" position: "); - snprintf(buffer, sizeof(buffer), "%g", this->position); - out.append(buffer); - out.append("\n"); - - out.append(" tilt: "); - snprintf(buffer, sizeof(buffer), "%g", this->tilt); - out.append(buffer); - out.append("\n"); - - out.append(" current_operation: "); - out.append(proto_enum_to_string(this->current_operation)); - out.append("\n"); - + MessageDumpHelper helper(out, "CoverStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "position", this->position); + dump_field(out, "tilt", this->tilt); + dump_field(out, "current_operation", static_cast(this->current_operation)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void CoverCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("CoverCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_position: "); - out.append(YESNO(this->has_position)); - out.append("\n"); - - out.append(" position: "); - snprintf(buffer, sizeof(buffer), "%g", this->position); - out.append(buffer); - out.append("\n"); - - out.append(" has_tilt: "); - out.append(YESNO(this->has_tilt)); - out.append("\n"); - - out.append(" tilt: "); - snprintf(buffer, sizeof(buffer), "%g", this->tilt); - out.append(buffer); - out.append("\n"); - - out.append(" stop: "); - out.append(YESNO(this->stop)); - out.append("\n"); - + MessageDumpHelper helper(out, "CoverCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_position", this->has_position); + dump_field(out, "position", this->position); + dump_field(out, "has_tilt", this->has_tilt); + dump_field(out, "tilt", this->tilt); + dump_field(out, "stop", this->stop); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_FAN void ListEntitiesFanResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesFanResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" supports_oscillation: "); - out.append(YESNO(this->supports_oscillation)); - out.append("\n"); - - out.append(" supports_speed: "); - out.append(YESNO(this->supports_speed)); - out.append("\n"); - - out.append(" supports_direction: "); - out.append(YESNO(this->supports_direction)); - out.append("\n"); - - out.append(" supported_speed_count: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->supported_speed_count); - out.append(buffer); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesFanResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); + dump_field(out, "supports_oscillation", this->supports_oscillation); + dump_field(out, "supports_speed", this->supports_speed); + dump_field(out, "supports_direction", this->supports_direction); + dump_field(out, "supported_speed_count", this->supported_speed_count); + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); for (const auto &it : this->supported_preset_modes) { - out.append(" supported_preset_modes: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "supported_preset_modes", it, 4); } - #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void FanStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("FanStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" oscillating: "); - out.append(YESNO(this->oscillating)); - out.append("\n"); - - out.append(" direction: "); - out.append(proto_enum_to_string(this->direction)); - out.append("\n"); - - out.append(" speed_level: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->speed_level); - out.append(buffer); - out.append("\n"); - - out.append(" preset_mode: "); - append_quoted_string(out, this->preset_mode_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "FanStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); + dump_field(out, "oscillating", this->oscillating); + dump_field(out, "direction", static_cast(this->direction)); + dump_field(out, "speed_level", this->speed_level); + dump_field(out, "preset_mode", this->preset_mode_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void FanCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("FanCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_state: "); - out.append(YESNO(this->has_state)); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" has_oscillating: "); - out.append(YESNO(this->has_oscillating)); - out.append("\n"); - - out.append(" oscillating: "); - out.append(YESNO(this->oscillating)); - out.append("\n"); - - out.append(" has_direction: "); - out.append(YESNO(this->has_direction)); - out.append("\n"); - - out.append(" direction: "); - out.append(proto_enum_to_string(this->direction)); - out.append("\n"); - - out.append(" has_speed_level: "); - out.append(YESNO(this->has_speed_level)); - out.append("\n"); - - out.append(" speed_level: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->speed_level); - out.append(buffer); - out.append("\n"); - - out.append(" has_preset_mode: "); - out.append(YESNO(this->has_preset_mode)); - out.append("\n"); - - out.append(" preset_mode: "); - out.append("'").append(this->preset_mode).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "FanCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_state", this->has_state); + dump_field(out, "state", this->state); + dump_field(out, "has_oscillating", this->has_oscillating); + dump_field(out, "oscillating", this->oscillating); + dump_field(out, "has_direction", this->has_direction); + dump_field(out, "direction", static_cast(this->direction)); + dump_field(out, "has_speed_level", this->has_speed_level); + dump_field(out, "speed_level", this->speed_level); + dump_field(out, "has_preset_mode", this->has_preset_mode); + dump_field(out, "preset_mode", this->preset_mode); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_LIGHT void ListEntitiesLightResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesLightResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesLightResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); for (const auto &it : this->supported_color_modes) { - out.append(" supported_color_modes: "); - out.append(proto_enum_to_string(it)); - out.append("\n"); + dump_field(out, "supported_color_modes", static_cast(it), 4); } - - out.append(" min_mireds: "); - snprintf(buffer, sizeof(buffer), "%g", this->min_mireds); - out.append(buffer); - out.append("\n"); - - out.append(" max_mireds: "); - snprintf(buffer, sizeof(buffer), "%g", this->max_mireds); - out.append(buffer); - out.append("\n"); - + dump_field(out, "min_mireds", this->min_mireds); + dump_field(out, "max_mireds", this->max_mireds); for (const auto &it : this->effects) { - out.append(" effects: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "effects", it, 4); } - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void LightStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("LightStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" brightness: "); - snprintf(buffer, sizeof(buffer), "%g", this->brightness); - out.append(buffer); - out.append("\n"); - - out.append(" color_mode: "); - out.append(proto_enum_to_string(this->color_mode)); - out.append("\n"); - - out.append(" color_brightness: "); - snprintf(buffer, sizeof(buffer), "%g", this->color_brightness); - out.append(buffer); - out.append("\n"); - - out.append(" red: "); - snprintf(buffer, sizeof(buffer), "%g", this->red); - out.append(buffer); - out.append("\n"); - - out.append(" green: "); - snprintf(buffer, sizeof(buffer), "%g", this->green); - out.append(buffer); - out.append("\n"); - - out.append(" blue: "); - snprintf(buffer, sizeof(buffer), "%g", this->blue); - out.append(buffer); - out.append("\n"); - - out.append(" white: "); - snprintf(buffer, sizeof(buffer), "%g", this->white); - out.append(buffer); - out.append("\n"); - - out.append(" color_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->color_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" cold_white: "); - snprintf(buffer, sizeof(buffer), "%g", this->cold_white); - out.append(buffer); - out.append("\n"); - - out.append(" warm_white: "); - snprintf(buffer, sizeof(buffer), "%g", this->warm_white); - out.append(buffer); - out.append("\n"); - - out.append(" effect: "); - append_quoted_string(out, this->effect_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "LightStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); + dump_field(out, "brightness", this->brightness); + dump_field(out, "color_mode", static_cast(this->color_mode)); + dump_field(out, "color_brightness", this->color_brightness); + dump_field(out, "red", this->red); + dump_field(out, "green", this->green); + dump_field(out, "blue", this->blue); + dump_field(out, "white", this->white); + dump_field(out, "color_temperature", this->color_temperature); + dump_field(out, "cold_white", this->cold_white); + dump_field(out, "warm_white", this->warm_white); + dump_field(out, "effect", this->effect_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void LightCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("LightCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_state: "); - out.append(YESNO(this->has_state)); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" has_brightness: "); - out.append(YESNO(this->has_brightness)); - out.append("\n"); - - out.append(" brightness: "); - snprintf(buffer, sizeof(buffer), "%g", this->brightness); - out.append(buffer); - out.append("\n"); - - out.append(" has_color_mode: "); - out.append(YESNO(this->has_color_mode)); - out.append("\n"); - - out.append(" color_mode: "); - out.append(proto_enum_to_string(this->color_mode)); - out.append("\n"); - - out.append(" has_color_brightness: "); - out.append(YESNO(this->has_color_brightness)); - out.append("\n"); - - out.append(" color_brightness: "); - snprintf(buffer, sizeof(buffer), "%g", this->color_brightness); - out.append(buffer); - out.append("\n"); - - out.append(" has_rgb: "); - out.append(YESNO(this->has_rgb)); - out.append("\n"); - - out.append(" red: "); - snprintf(buffer, sizeof(buffer), "%g", this->red); - out.append(buffer); - out.append("\n"); - - out.append(" green: "); - snprintf(buffer, sizeof(buffer), "%g", this->green); - out.append(buffer); - out.append("\n"); - - out.append(" blue: "); - snprintf(buffer, sizeof(buffer), "%g", this->blue); - out.append(buffer); - out.append("\n"); - - out.append(" has_white: "); - out.append(YESNO(this->has_white)); - out.append("\n"); - - out.append(" white: "); - snprintf(buffer, sizeof(buffer), "%g", this->white); - out.append(buffer); - out.append("\n"); - - out.append(" has_color_temperature: "); - out.append(YESNO(this->has_color_temperature)); - out.append("\n"); - - out.append(" color_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->color_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" has_cold_white: "); - out.append(YESNO(this->has_cold_white)); - out.append("\n"); - - out.append(" cold_white: "); - snprintf(buffer, sizeof(buffer), "%g", this->cold_white); - out.append(buffer); - out.append("\n"); - - out.append(" has_warm_white: "); - out.append(YESNO(this->has_warm_white)); - out.append("\n"); - - out.append(" warm_white: "); - snprintf(buffer, sizeof(buffer), "%g", this->warm_white); - out.append(buffer); - out.append("\n"); - - out.append(" has_transition_length: "); - out.append(YESNO(this->has_transition_length)); - out.append("\n"); - - out.append(" transition_length: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->transition_length); - out.append(buffer); - out.append("\n"); - - out.append(" has_flash_length: "); - out.append(YESNO(this->has_flash_length)); - out.append("\n"); - - out.append(" flash_length: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->flash_length); - out.append(buffer); - out.append("\n"); - - out.append(" has_effect: "); - out.append(YESNO(this->has_effect)); - out.append("\n"); - - out.append(" effect: "); - out.append("'").append(this->effect).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "LightCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_state", this->has_state); + dump_field(out, "state", this->state); + dump_field(out, "has_brightness", this->has_brightness); + dump_field(out, "brightness", this->brightness); + dump_field(out, "has_color_mode", this->has_color_mode); + dump_field(out, "color_mode", static_cast(this->color_mode)); + dump_field(out, "has_color_brightness", this->has_color_brightness); + dump_field(out, "color_brightness", this->color_brightness); + dump_field(out, "has_rgb", this->has_rgb); + dump_field(out, "red", this->red); + dump_field(out, "green", this->green); + dump_field(out, "blue", this->blue); + dump_field(out, "has_white", this->has_white); + dump_field(out, "white", this->white); + dump_field(out, "has_color_temperature", this->has_color_temperature); + dump_field(out, "color_temperature", this->color_temperature); + dump_field(out, "has_cold_white", this->has_cold_white); + dump_field(out, "cold_white", this->cold_white); + dump_field(out, "has_warm_white", this->has_warm_white); + dump_field(out, "warm_white", this->warm_white); + dump_field(out, "has_transition_length", this->has_transition_length); + dump_field(out, "transition_length", this->transition_length); + dump_field(out, "has_flash_length", this->has_flash_length); + dump_field(out, "flash_length", this->flash_length); + dump_field(out, "has_effect", this->has_effect); + dump_field(out, "effect", this->effect); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_SENSOR void ListEntitiesSensorResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesSensorResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesSensorResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" unit_of_measurement: "); - append_quoted_string(out, this->unit_of_measurement_ref_); - out.append("\n"); - - out.append(" accuracy_decimals: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->accuracy_decimals); - out.append(buffer); - out.append("\n"); - - out.append(" force_update: "); - out.append(YESNO(this->force_update)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - - out.append(" state_class: "); - out.append(proto_enum_to_string(this->state_class)); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "unit_of_measurement", this->unit_of_measurement_ref_); + dump_field(out, "accuracy_decimals", this->accuracy_decimals); + dump_field(out, "force_update", this->force_update); + dump_field(out, "device_class", this->device_class_ref_); + dump_field(out, "state_class", static_cast(this->state_class)); + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SensorStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SensorStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - snprintf(buffer, sizeof(buffer), "%g", this->state); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "SensorStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_SWITCH void ListEntitiesSwitchResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesSwitchResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesSwitchResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" assumed_state: "); - out.append(YESNO(this->assumed_state)); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "assumed_state", this->assumed_state); + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SwitchStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SwitchStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - + MessageDumpHelper helper(out, "SwitchStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SwitchCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SwitchCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - + MessageDumpHelper helper(out, "SwitchCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_TEXT_SENSOR void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesTextSensorResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesTextSensorResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void TextSensorStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("TextSensorStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - append_quoted_string(out, this->state_ref_); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "TextSensorStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state_ref_); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif void SubscribeLogsRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SubscribeLogsRequest {\n"); - out.append(" level: "); - out.append(proto_enum_to_string(this->level)); - out.append("\n"); - - out.append(" dump_config: "); - out.append(YESNO(this->dump_config)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "SubscribeLogsRequest"); + dump_field(out, "level", static_cast(this->level)); + dump_field(out, "dump_config", this->dump_config); } void SubscribeLogsResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SubscribeLogsResponse {\n"); - out.append(" level: "); - out.append(proto_enum_to_string(this->level)); - out.append("\n"); - + MessageDumpHelper helper(out, "SubscribeLogsResponse"); + dump_field(out, "level", static_cast(this->level)); out.append(" message: "); out.append(format_hex_pretty(this->message_ptr_, this->message_len_)); out.append("\n"); - out.append("}"); } #ifdef USE_API_NOISE void NoiseEncryptionSetKeyRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("NoiseEncryptionSetKeyRequest {\n"); + MessageDumpHelper helper(out, "NoiseEncryptionSetKeyRequest"); out.append(" key: "); out.append(format_hex_pretty(reinterpret_cast(this->key.data()), this->key.size())); out.append("\n"); - out.append("}"); -} -void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("NoiseEncryptionSetKeyResponse {\n"); - out.append(" success: "); - out.append(YESNO(this->success)); - out.append("\n"); - out.append("}"); } +void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const { dump_field(out, "success", this->success); } #endif void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const { out.append("SubscribeHomeassistantServicesRequest {}"); } void HomeassistantServiceMap::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("HomeassistantServiceMap {\n"); - out.append(" key: "); - append_quoted_string(out, this->key_ref_); - out.append("\n"); - - out.append(" value: "); - append_quoted_string(out, this->value_ref_); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "HomeassistantServiceMap"); + dump_field(out, "key", this->key_ref_); + dump_field(out, "value", this->value_ref_); } void HomeassistantServiceResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("HomeassistantServiceResponse {\n"); - out.append(" service: "); - append_quoted_string(out, this->service_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "HomeassistantServiceResponse"); + dump_field(out, "service", this->service_ref_); for (const auto &it : this->data) { out.append(" data: "); it.dump_to(out); out.append("\n"); } - for (const auto &it : this->data_template) { out.append(" data_template: "); it.dump_to(out); out.append("\n"); } - for (const auto &it : this->variables) { out.append(" variables: "); it.dump_to(out); out.append("\n"); } - - out.append(" is_event: "); - out.append(YESNO(this->is_event)); - out.append("\n"); - out.append("}"); + dump_field(out, "is_event", this->is_event); } void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { out.append("SubscribeHomeAssistantStatesRequest {}"); } void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SubscribeHomeAssistantStateResponse {\n"); - out.append(" entity_id: "); - append_quoted_string(out, this->entity_id_ref_); - out.append("\n"); - - out.append(" attribute: "); - append_quoted_string(out, this->attribute_ref_); - out.append("\n"); - - out.append(" once: "); - out.append(YESNO(this->once)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "SubscribeHomeAssistantStateResponse"); + dump_field(out, "entity_id", this->entity_id_ref_); + dump_field(out, "attribute", this->attribute_ref_); + dump_field(out, "once", this->once); } void HomeAssistantStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("HomeAssistantStateResponse {\n"); - out.append(" entity_id: "); - out.append("'").append(this->entity_id).append("'"); - out.append("\n"); - - out.append(" state: "); - out.append("'").append(this->state).append("'"); - out.append("\n"); - - out.append(" attribute: "); - out.append("'").append(this->attribute).append("'"); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "HomeAssistantStateResponse"); + dump_field(out, "entity_id", this->entity_id); + dump_field(out, "state", this->state); + dump_field(out, "attribute", this->attribute); } void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); } -void GetTimeResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("GetTimeResponse {\n"); - out.append(" epoch_seconds: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->epoch_seconds); - out.append(buffer); - out.append("\n"); - out.append("}"); -} +void GetTimeResponse::dump_to(std::string &out) const { dump_field(out, "epoch_seconds", this->epoch_seconds); } #ifdef USE_API_SERVICES void ListEntitiesServicesArgument::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesServicesArgument {\n"); - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" type: "); - out.append(proto_enum_to_string(this->type)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "ListEntitiesServicesArgument"); + dump_field(out, "name", this->name_ref_); + dump_field(out, "type", static_cast(this->type)); } void ListEntitiesServicesResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesServicesResponse {\n"); - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesServicesResponse"); + dump_field(out, "name", this->name_ref_); + dump_field(out, "key", this->key); for (const auto &it : this->args) { out.append(" args: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void ExecuteServiceArgument::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ExecuteServiceArgument {\n"); - out.append(" bool_: "); - out.append(YESNO(this->bool_)); - out.append("\n"); - - out.append(" legacy_int: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->legacy_int); - out.append(buffer); - out.append("\n"); - - out.append(" float_: "); - snprintf(buffer, sizeof(buffer), "%g", this->float_); - out.append(buffer); - out.append("\n"); - - out.append(" string_: "); - out.append("'").append(this->string_).append("'"); - out.append("\n"); - - out.append(" int_: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->int_); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ExecuteServiceArgument"); + dump_field(out, "bool_", this->bool_); + dump_field(out, "legacy_int", this->legacy_int); + dump_field(out, "float_", this->float_); + dump_field(out, "string_", this->string_); + dump_field(out, "int_", this->int_); for (const auto it : this->bool_array) { - out.append(" bool_array: "); - out.append(YESNO(it)); - out.append("\n"); + dump_field(out, "bool_array", it, 4); } - for (const auto &it : this->int_array) { - out.append(" int_array: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, it); - out.append(buffer); - out.append("\n"); + dump_field(out, "int_array", it, 4); } - for (const auto &it : this->float_array) { - out.append(" float_array: "); - snprintf(buffer, sizeof(buffer), "%g", it); - out.append(buffer); - out.append("\n"); + dump_field(out, "float_array", it, 4); } - for (const auto &it : this->string_array) { - out.append(" string_array: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "string_array", it, 4); } - out.append("}"); } void ExecuteServiceRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ExecuteServiceRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ExecuteServiceRequest"); + dump_field(out, "key", this->key); for (const auto &it : this->args) { out.append(" args: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } #endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesCameraResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesCameraResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void CameraImageResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("CameraImageResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "CameraImageResponse"); + dump_field(out, "key", this->key); out.append(" data: "); out.append(format_hex_pretty(this->data_ptr_, this->data_len_)); out.append("\n"); - - out.append(" done: "); - out.append(YESNO(this->done)); - out.append("\n"); - + dump_field(out, "done", this->done); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void CameraImageRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("CameraImageRequest {\n"); - out.append(" single: "); - out.append(YESNO(this->single)); - out.append("\n"); - - out.append(" stream: "); - out.append(YESNO(this->stream)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "CameraImageRequest"); + dump_field(out, "single", this->single); + dump_field(out, "stream", this->stream); } #endif #ifdef USE_CLIMATE void ListEntitiesClimateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesClimateResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - - out.append(" supports_current_temperature: "); - out.append(YESNO(this->supports_current_temperature)); - out.append("\n"); - - out.append(" supports_two_point_target_temperature: "); - out.append(YESNO(this->supports_two_point_target_temperature)); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesClimateResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); + dump_field(out, "supports_current_temperature", this->supports_current_temperature); + dump_field(out, "supports_two_point_target_temperature", this->supports_two_point_target_temperature); for (const auto &it : this->supported_modes) { - out.append(" supported_modes: "); - out.append(proto_enum_to_string(it)); - out.append("\n"); + dump_field(out, "supported_modes", static_cast(it), 4); } - - out.append(" visual_min_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_min_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" visual_max_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_max_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" visual_target_temperature_step: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_target_temperature_step); - out.append(buffer); - out.append("\n"); - - out.append(" supports_action: "); - out.append(YESNO(this->supports_action)); - out.append("\n"); - + dump_field(out, "visual_min_temperature", this->visual_min_temperature); + dump_field(out, "visual_max_temperature", this->visual_max_temperature); + dump_field(out, "visual_target_temperature_step", this->visual_target_temperature_step); + dump_field(out, "supports_action", this->supports_action); for (const auto &it : this->supported_fan_modes) { - out.append(" supported_fan_modes: "); - out.append(proto_enum_to_string(it)); - out.append("\n"); + dump_field(out, "supported_fan_modes", static_cast(it), 4); } - for (const auto &it : this->supported_swing_modes) { - out.append(" supported_swing_modes: "); - out.append(proto_enum_to_string(it)); - out.append("\n"); + dump_field(out, "supported_swing_modes", static_cast(it), 4); } - for (const auto &it : this->supported_custom_fan_modes) { - out.append(" supported_custom_fan_modes: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "supported_custom_fan_modes", it, 4); } - for (const auto &it : this->supported_presets) { - out.append(" supported_presets: "); - out.append(proto_enum_to_string(it)); - out.append("\n"); + dump_field(out, "supported_presets", static_cast(it), 4); } - for (const auto &it : this->supported_custom_presets) { - out.append(" supported_custom_presets: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "supported_custom_presets", it, 4); } - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" visual_current_temperature_step: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_current_temperature_step); - out.append(buffer); - out.append("\n"); - - out.append(" supports_current_humidity: "); - out.append(YESNO(this->supports_current_humidity)); - out.append("\n"); - - out.append(" supports_target_humidity: "); - out.append(YESNO(this->supports_target_humidity)); - out.append("\n"); - - out.append(" visual_min_humidity: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_min_humidity); - out.append(buffer); - out.append("\n"); - - out.append(" visual_max_humidity: "); - snprintf(buffer, sizeof(buffer), "%g", this->visual_max_humidity); - out.append(buffer); - out.append("\n"); - + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "visual_current_temperature_step", this->visual_current_temperature_step); + dump_field(out, "supports_current_humidity", this->supports_current_humidity); + dump_field(out, "supports_target_humidity", this->supports_target_humidity); + dump_field(out, "visual_min_humidity", this->visual_min_humidity); + dump_field(out, "visual_max_humidity", this->visual_max_humidity); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void ClimateStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ClimateStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - - out.append(" current_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->current_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" target_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" target_temperature_low: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature_low); - out.append(buffer); - out.append("\n"); - - out.append(" target_temperature_high: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature_high); - out.append(buffer); - out.append("\n"); - - out.append(" action: "); - out.append(proto_enum_to_string(this->action)); - out.append("\n"); - - out.append(" fan_mode: "); - out.append(proto_enum_to_string(this->fan_mode)); - out.append("\n"); - - out.append(" swing_mode: "); - out.append(proto_enum_to_string(this->swing_mode)); - out.append("\n"); - - out.append(" custom_fan_mode: "); - append_quoted_string(out, this->custom_fan_mode_ref_); - out.append("\n"); - - out.append(" preset: "); - out.append(proto_enum_to_string(this->preset)); - out.append("\n"); - - out.append(" custom_preset: "); - append_quoted_string(out, this->custom_preset_ref_); - out.append("\n"); - - out.append(" current_humidity: "); - snprintf(buffer, sizeof(buffer), "%g", this->current_humidity); - out.append(buffer); - out.append("\n"); - - out.append(" target_humidity: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_humidity); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ClimateStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "mode", static_cast(this->mode)); + dump_field(out, "current_temperature", this->current_temperature); + dump_field(out, "target_temperature", this->target_temperature); + dump_field(out, "target_temperature_low", this->target_temperature_low); + dump_field(out, "target_temperature_high", this->target_temperature_high); + dump_field(out, "action", static_cast(this->action)); + dump_field(out, "fan_mode", static_cast(this->fan_mode)); + dump_field(out, "swing_mode", static_cast(this->swing_mode)); + dump_field(out, "custom_fan_mode", this->custom_fan_mode_ref_); + dump_field(out, "preset", static_cast(this->preset)); + dump_field(out, "custom_preset", this->custom_preset_ref_); + dump_field(out, "current_humidity", this->current_humidity); + dump_field(out, "target_humidity", this->target_humidity); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void ClimateCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ClimateCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_mode: "); - out.append(YESNO(this->has_mode)); - out.append("\n"); - - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - - out.append(" has_target_temperature: "); - out.append(YESNO(this->has_target_temperature)); - out.append("\n"); - - out.append(" target_temperature: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature); - out.append(buffer); - out.append("\n"); - - out.append(" has_target_temperature_low: "); - out.append(YESNO(this->has_target_temperature_low)); - out.append("\n"); - - out.append(" target_temperature_low: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature_low); - out.append(buffer); - out.append("\n"); - - out.append(" has_target_temperature_high: "); - out.append(YESNO(this->has_target_temperature_high)); - out.append("\n"); - - out.append(" target_temperature_high: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_temperature_high); - out.append(buffer); - out.append("\n"); - - out.append(" has_fan_mode: "); - out.append(YESNO(this->has_fan_mode)); - out.append("\n"); - - out.append(" fan_mode: "); - out.append(proto_enum_to_string(this->fan_mode)); - out.append("\n"); - - out.append(" has_swing_mode: "); - out.append(YESNO(this->has_swing_mode)); - out.append("\n"); - - out.append(" swing_mode: "); - out.append(proto_enum_to_string(this->swing_mode)); - out.append("\n"); - - out.append(" has_custom_fan_mode: "); - out.append(YESNO(this->has_custom_fan_mode)); - out.append("\n"); - - out.append(" custom_fan_mode: "); - out.append("'").append(this->custom_fan_mode).append("'"); - out.append("\n"); - - out.append(" has_preset: "); - out.append(YESNO(this->has_preset)); - out.append("\n"); - - out.append(" preset: "); - out.append(proto_enum_to_string(this->preset)); - out.append("\n"); - - out.append(" has_custom_preset: "); - out.append(YESNO(this->has_custom_preset)); - out.append("\n"); - - out.append(" custom_preset: "); - out.append("'").append(this->custom_preset).append("'"); - out.append("\n"); - - out.append(" has_target_humidity: "); - out.append(YESNO(this->has_target_humidity)); - out.append("\n"); - - out.append(" target_humidity: "); - snprintf(buffer, sizeof(buffer), "%g", this->target_humidity); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ClimateCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_mode", this->has_mode); + dump_field(out, "mode", static_cast(this->mode)); + dump_field(out, "has_target_temperature", this->has_target_temperature); + dump_field(out, "target_temperature", this->target_temperature); + dump_field(out, "has_target_temperature_low", this->has_target_temperature_low); + dump_field(out, "target_temperature_low", this->target_temperature_low); + dump_field(out, "has_target_temperature_high", this->has_target_temperature_high); + dump_field(out, "target_temperature_high", this->target_temperature_high); + dump_field(out, "has_fan_mode", this->has_fan_mode); + dump_field(out, "fan_mode", static_cast(this->fan_mode)); + dump_field(out, "has_swing_mode", this->has_swing_mode); + dump_field(out, "swing_mode", static_cast(this->swing_mode)); + dump_field(out, "has_custom_fan_mode", this->has_custom_fan_mode); + dump_field(out, "custom_fan_mode", this->custom_fan_mode); + dump_field(out, "has_preset", this->has_preset); + dump_field(out, "preset", static_cast(this->preset)); + dump_field(out, "has_custom_preset", this->has_custom_preset); + dump_field(out, "custom_preset", this->custom_preset); + dump_field(out, "has_target_humidity", this->has_target_humidity); + dump_field(out, "target_humidity", this->target_humidity); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_NUMBER void ListEntitiesNumberResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesNumberResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesNumberResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" min_value: "); - snprintf(buffer, sizeof(buffer), "%g", this->min_value); - out.append(buffer); - out.append("\n"); - - out.append(" max_value: "); - snprintf(buffer, sizeof(buffer), "%g", this->max_value); - out.append(buffer); - out.append("\n"); - - out.append(" step: "); - snprintf(buffer, sizeof(buffer), "%g", this->step); - out.append(buffer); - out.append("\n"); - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" unit_of_measurement: "); - append_quoted_string(out, this->unit_of_measurement_ref_); - out.append("\n"); - - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "min_value", this->min_value); + dump_field(out, "max_value", this->max_value); + dump_field(out, "step", this->step); + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "unit_of_measurement", this->unit_of_measurement_ref_); + dump_field(out, "mode", static_cast(this->mode)); + dump_field(out, "device_class", this->device_class_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void NumberStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("NumberStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - snprintf(buffer, sizeof(buffer), "%g", this->state); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "NumberStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void NumberCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("NumberCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - snprintf(buffer, sizeof(buffer), "%g", this->state); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "NumberCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_SELECT void ListEntitiesSelectResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesSelectResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesSelectResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif for (const auto &it : this->options) { - out.append(" options: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "options", it, 4); } - - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SelectStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SelectStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - append_quoted_string(out, this->state_ref_); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "SelectStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state_ref_); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SelectCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SelectCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append("'").append(this->state).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "SelectCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_SIREN void ListEntitiesSirenResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesSirenResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesSirenResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); for (const auto &it : this->tones) { - out.append(" tones: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "tones", it, 4); } - - out.append(" supports_duration: "); - out.append(YESNO(this->supports_duration)); - out.append("\n"); - - out.append(" supports_volume: "); - out.append(YESNO(this->supports_volume)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "supports_duration", this->supports_duration); + dump_field(out, "supports_volume", this->supports_volume); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SirenStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SirenStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - + MessageDumpHelper helper(out, "SirenStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void SirenCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SirenCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_state: "); - out.append(YESNO(this->has_state)); - out.append("\n"); - - out.append(" state: "); - out.append(YESNO(this->state)); - out.append("\n"); - - out.append(" has_tone: "); - out.append(YESNO(this->has_tone)); - out.append("\n"); - - out.append(" tone: "); - out.append("'").append(this->tone).append("'"); - out.append("\n"); - - out.append(" has_duration: "); - out.append(YESNO(this->has_duration)); - out.append("\n"); - - out.append(" duration: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->duration); - out.append(buffer); - out.append("\n"); - - out.append(" has_volume: "); - out.append(YESNO(this->has_volume)); - out.append("\n"); - - out.append(" volume: "); - snprintf(buffer, sizeof(buffer), "%g", this->volume); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "SirenCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_state", this->has_state); + dump_field(out, "state", this->state); + dump_field(out, "has_tone", this->has_tone); + dump_field(out, "tone", this->tone); + dump_field(out, "has_duration", this->has_duration); + dump_field(out, "duration", this->duration); + dump_field(out, "has_volume", this->has_volume); + dump_field(out, "volume", this->volume); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_LOCK void ListEntitiesLockResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesLockResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesLockResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" assumed_state: "); - out.append(YESNO(this->assumed_state)); - out.append("\n"); - - out.append(" supports_open: "); - out.append(YESNO(this->supports_open)); - out.append("\n"); - - out.append(" requires_code: "); - out.append(YESNO(this->requires_code)); - out.append("\n"); - - out.append(" code_format: "); - append_quoted_string(out, this->code_format_ref_); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "assumed_state", this->assumed_state); + dump_field(out, "supports_open", this->supports_open); + dump_field(out, "requires_code", this->requires_code); + dump_field(out, "code_format", this->code_format_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void LockStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("LockStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(proto_enum_to_string(this->state)); - out.append("\n"); - + MessageDumpHelper helper(out, "LockStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", static_cast(this->state)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void LockCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("LockCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" command: "); - out.append(proto_enum_to_string(this->command)); - out.append("\n"); - - out.append(" has_code: "); - out.append(YESNO(this->has_code)); - out.append("\n"); - - out.append(" code: "); - out.append("'").append(this->code).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "LockCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "command", static_cast(this->command)); + dump_field(out, "has_code", this->has_code); + dump_field(out, "code", this->code); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_BUTTON void ListEntitiesButtonResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesButtonResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesButtonResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void ButtonCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ButtonCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "ButtonCommandRequest"); + dump_field(out, "key", this->key); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_MEDIA_PLAYER void MediaPlayerSupportedFormat::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("MediaPlayerSupportedFormat {\n"); - out.append(" format: "); - append_quoted_string(out, this->format_ref_); - out.append("\n"); - - out.append(" sample_rate: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->sample_rate); - out.append(buffer); - out.append("\n"); - - out.append(" num_channels: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->num_channels); - out.append(buffer); - out.append("\n"); - - out.append(" purpose: "); - out.append(proto_enum_to_string(this->purpose)); - out.append("\n"); - - out.append(" sample_bytes: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->sample_bytes); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "MediaPlayerSupportedFormat"); + dump_field(out, "format", this->format_ref_); + dump_field(out, "sample_rate", this->sample_rate); + dump_field(out, "num_channels", this->num_channels); + dump_field(out, "purpose", static_cast(this->purpose)); + dump_field(out, "sample_bytes", this->sample_bytes); } void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesMediaPlayerResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesMediaPlayerResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" supports_pause: "); - out.append(YESNO(this->supports_pause)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "supports_pause", this->supports_pause); for (const auto &it : this->supported_formats) { out.append(" supported_formats: "); it.dump_to(out); out.append("\n"); } - #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void MediaPlayerStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("MediaPlayerStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(proto_enum_to_string(this->state)); - out.append("\n"); - - out.append(" volume: "); - snprintf(buffer, sizeof(buffer), "%g", this->volume); - out.append(buffer); - out.append("\n"); - - out.append(" muted: "); - out.append(YESNO(this->muted)); - out.append("\n"); - + MessageDumpHelper helper(out, "MediaPlayerStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", static_cast(this->state)); + dump_field(out, "volume", this->volume); + dump_field(out, "muted", this->muted); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void MediaPlayerCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("MediaPlayerCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_command: "); - out.append(YESNO(this->has_command)); - out.append("\n"); - - out.append(" command: "); - out.append(proto_enum_to_string(this->command)); - out.append("\n"); - - out.append(" has_volume: "); - out.append(YESNO(this->has_volume)); - out.append("\n"); - - out.append(" volume: "); - snprintf(buffer, sizeof(buffer), "%g", this->volume); - out.append(buffer); - out.append("\n"); - - out.append(" has_media_url: "); - out.append(YESNO(this->has_media_url)); - out.append("\n"); - - out.append(" media_url: "); - out.append("'").append(this->media_url).append("'"); - out.append("\n"); - - out.append(" has_announcement: "); - out.append(YESNO(this->has_announcement)); - out.append("\n"); - - out.append(" announcement: "); - out.append(YESNO(this->announcement)); - out.append("\n"); - + MessageDumpHelper helper(out, "MediaPlayerCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_command", this->has_command); + dump_field(out, "command", static_cast(this->command)); + dump_field(out, "has_volume", this->has_volume); + dump_field(out, "volume", this->volume); + dump_field(out, "has_media_url", this->has_media_url); + dump_field(out, "media_url", this->media_url); + dump_field(out, "has_announcement", this->has_announcement); + dump_field(out, "announcement", this->announcement); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_BLUETOOTH_PROXY void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SubscribeBluetoothLEAdvertisementsRequest {\n"); - out.append(" flags: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->flags); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "SubscribeBluetoothLEAdvertisementsRequest"); + dump_field(out, "flags", this->flags); } void BluetoothLERawAdvertisement::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothLERawAdvertisement {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" rssi: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->rssi); - out.append(buffer); - out.append("\n"); - - out.append(" address_type: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->address_type); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothLERawAdvertisement"); + dump_field(out, "address", this->address); + dump_field(out, "rssi", this->rssi); + dump_field(out, "address_type", this->address_type); out.append(" data: "); out.append(format_hex_pretty(this->data, this->data_len)); out.append("\n"); - out.append("}"); } void BluetoothLERawAdvertisementsResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothLERawAdvertisementsResponse {\n"); + MessageDumpHelper helper(out, "BluetoothLERawAdvertisementsResponse"); for (const auto &it : this->advertisements) { out.append(" advertisements: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void BluetoothDeviceRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothDeviceRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" request_type: "); - out.append(proto_enum_to_string(this->request_type)); - out.append("\n"); - - out.append(" has_address_type: "); - out.append(YESNO(this->has_address_type)); - out.append("\n"); - - out.append(" address_type: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->address_type); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothDeviceRequest"); + dump_field(out, "address", this->address); + dump_field(out, "request_type", static_cast(this->request_type)); + dump_field(out, "has_address_type", this->has_address_type); + dump_field(out, "address_type", this->address_type); } void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothDeviceConnectionResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" connected: "); - out.append(YESNO(this->connected)); - out.append("\n"); - - out.append(" mtu: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->mtu); - out.append(buffer); - out.append("\n"); - - out.append(" error: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->error); - out.append(buffer); - out.append("\n"); - out.append("}"); -} -void BluetoothGATTGetServicesRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTGetServicesRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothDeviceConnectionResponse"); + dump_field(out, "address", this->address); + dump_field(out, "connected", this->connected); + dump_field(out, "mtu", this->mtu); + dump_field(out, "error", this->error); } +void BluetoothGATTGetServicesRequest::dump_to(std::string &out) const { dump_field(out, "address", this->address); } void BluetoothGATTDescriptor::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTDescriptor {\n"); + MessageDumpHelper helper(out, "BluetoothGATTDescriptor"); for (const auto &it : this->uuid) { - out.append(" uuid: "); - snprintf(buffer, sizeof(buffer), "%llu", it); - out.append(buffer); - out.append("\n"); + dump_field(out, "uuid", it, 4); } - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - out.append("}"); + dump_field(out, "handle", this->handle); } void BluetoothGATTCharacteristic::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTCharacteristic {\n"); + MessageDumpHelper helper(out, "BluetoothGATTCharacteristic"); for (const auto &it : this->uuid) { - out.append(" uuid: "); - snprintf(buffer, sizeof(buffer), "%llu", it); - out.append(buffer); - out.append("\n"); + dump_field(out, "uuid", it, 4); } - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - - out.append(" properties: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->properties); - out.append(buffer); - out.append("\n"); - + dump_field(out, "handle", this->handle); + dump_field(out, "properties", this->properties); for (const auto &it : this->descriptors) { out.append(" descriptors: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void BluetoothGATTService::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTService {\n"); + MessageDumpHelper helper(out, "BluetoothGATTService"); for (const auto &it : this->uuid) { - out.append(" uuid: "); - snprintf(buffer, sizeof(buffer), "%llu", it); - out.append(buffer); - out.append("\n"); + dump_field(out, "uuid", it, 4); } - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - + dump_field(out, "handle", this->handle); for (const auto &it : this->characteristics) { out.append(" characteristics: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void BluetoothGATTGetServicesResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTGetServicesResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothGATTGetServicesResponse"); + dump_field(out, "address", this->address); for (const auto &it : this->services) { out.append(" services: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void BluetoothGATTGetServicesDoneResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTGetServicesDoneResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTGetServicesDoneResponse"); + dump_field(out, "address", this->address); } void BluetoothGATTReadRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTReadRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTReadRequest"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); } void BluetoothGATTReadResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTReadResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothGATTReadResponse"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); out.append(" data: "); out.append(format_hex_pretty(this->data_ptr_, this->data_len_)); out.append("\n"); - out.append("}"); } void BluetoothGATTWriteRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTWriteRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - - out.append(" response: "); - out.append(YESNO(this->response)); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothGATTWriteRequest"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); + dump_field(out, "response", this->response); out.append(" data: "); out.append(format_hex_pretty(reinterpret_cast(this->data.data()), this->data.size())); out.append("\n"); - out.append("}"); } void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTReadDescriptorRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTReadDescriptorRequest"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); } void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTWriteDescriptorRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothGATTWriteDescriptorRequest"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); out.append(" data: "); out.append(format_hex_pretty(reinterpret_cast(this->data.data()), this->data.size())); out.append("\n"); - out.append("}"); } void BluetoothGATTNotifyRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTNotifyRequest {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - - out.append(" enable: "); - out.append(YESNO(this->enable)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTNotifyRequest"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); + dump_field(out, "enable", this->enable); } void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTNotifyDataResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothGATTNotifyDataResponse"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); out.append(" data: "); out.append(format_hex_pretty(this->data_ptr_, this->data_len_)); out.append("\n"); - out.append("}"); } void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const { out.append("SubscribeBluetoothConnectionsFreeRequest {}"); } void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothConnectionsFreeResponse {\n"); - out.append(" free: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->free); - out.append(buffer); - out.append("\n"); - - out.append(" limit: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->limit); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "BluetoothConnectionsFreeResponse"); + dump_field(out, "free", this->free); + dump_field(out, "limit", this->limit); for (const auto &it : this->allocated) { - out.append(" allocated: "); - snprintf(buffer, sizeof(buffer), "%llu", it); - out.append(buffer); - out.append("\n"); + dump_field(out, "allocated", it, 4); } - out.append("}"); } void BluetoothGATTErrorResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTErrorResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - - out.append(" error: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->error); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTErrorResponse"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); + dump_field(out, "error", this->error); } void BluetoothGATTWriteResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTWriteResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTWriteResponse"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); } void BluetoothGATTNotifyResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothGATTNotifyResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" handle: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->handle); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothGATTNotifyResponse"); + dump_field(out, "address", this->address); + dump_field(out, "handle", this->handle); } void BluetoothDevicePairingResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothDevicePairingResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" paired: "); - out.append(YESNO(this->paired)); - out.append("\n"); - - out.append(" error: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->error); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothDevicePairingResponse"); + dump_field(out, "address", this->address); + dump_field(out, "paired", this->paired); + dump_field(out, "error", this->error); } void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothDeviceUnpairingResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" success: "); - out.append(YESNO(this->success)); - out.append("\n"); - - out.append(" error: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->error); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothDeviceUnpairingResponse"); + dump_field(out, "address", this->address); + dump_field(out, "success", this->success); + dump_field(out, "error", this->error); } void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}"); } void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothDeviceClearCacheResponse {\n"); - out.append(" address: "); - snprintf(buffer, sizeof(buffer), "%llu", this->address); - out.append(buffer); - out.append("\n"); - - out.append(" success: "); - out.append(YESNO(this->success)); - out.append("\n"); - - out.append(" error: "); - snprintf(buffer, sizeof(buffer), "%" PRId32, this->error); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothDeviceClearCacheResponse"); + dump_field(out, "address", this->address); + dump_field(out, "success", this->success); + dump_field(out, "error", this->error); } void BluetoothScannerStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothScannerStateResponse {\n"); - out.append(" state: "); - out.append(proto_enum_to_string(this->state)); - out.append("\n"); - - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothScannerStateResponse"); + dump_field(out, "state", static_cast(this->state)); + dump_field(out, "mode", static_cast(this->mode)); } void BluetoothScannerSetModeRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("BluetoothScannerSetModeRequest {\n"); - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "BluetoothScannerSetModeRequest"); + dump_field(out, "mode", static_cast(this->mode)); } #endif #ifdef USE_VOICE_ASSISTANT void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("SubscribeVoiceAssistantRequest {\n"); - out.append(" subscribe: "); - out.append(YESNO(this->subscribe)); - out.append("\n"); - - out.append(" flags: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->flags); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "SubscribeVoiceAssistantRequest"); + dump_field(out, "subscribe", this->subscribe); + dump_field(out, "flags", this->flags); } void VoiceAssistantAudioSettings::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantAudioSettings {\n"); - out.append(" noise_suppression_level: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->noise_suppression_level); - out.append(buffer); - out.append("\n"); - - out.append(" auto_gain: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->auto_gain); - out.append(buffer); - out.append("\n"); - - out.append(" volume_multiplier: "); - snprintf(buffer, sizeof(buffer), "%g", this->volume_multiplier); - out.append(buffer); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "VoiceAssistantAudioSettings"); + dump_field(out, "noise_suppression_level", this->noise_suppression_level); + dump_field(out, "auto_gain", this->auto_gain); + dump_field(out, "volume_multiplier", this->volume_multiplier); } void VoiceAssistantRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantRequest {\n"); - out.append(" start: "); - out.append(YESNO(this->start)); - out.append("\n"); - - out.append(" conversation_id: "); - append_quoted_string(out, this->conversation_id_ref_); - out.append("\n"); - - out.append(" flags: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->flags); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "VoiceAssistantRequest"); + dump_field(out, "start", this->start); + dump_field(out, "conversation_id", this->conversation_id_ref_); + dump_field(out, "flags", this->flags); out.append(" audio_settings: "); this->audio_settings.dump_to(out); out.append("\n"); - - out.append(" wake_word_phrase: "); - append_quoted_string(out, this->wake_word_phrase_ref_); - out.append("\n"); - out.append("}"); + dump_field(out, "wake_word_phrase", this->wake_word_phrase_ref_); } void VoiceAssistantResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantResponse {\n"); - out.append(" port: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->port); - out.append(buffer); - out.append("\n"); - - out.append(" error: "); - out.append(YESNO(this->error)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "VoiceAssistantResponse"); + dump_field(out, "port", this->port); + dump_field(out, "error", this->error); } void VoiceAssistantEventData::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantEventData {\n"); - out.append(" name: "); - out.append("'").append(this->name).append("'"); - out.append("\n"); - - out.append(" value: "); - out.append("'").append(this->value).append("'"); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "VoiceAssistantEventData"); + dump_field(out, "name", this->name); + dump_field(out, "value", this->value); } void VoiceAssistantEventResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantEventResponse {\n"); - out.append(" event_type: "); - out.append(proto_enum_to_string(this->event_type)); - out.append("\n"); - + MessageDumpHelper helper(out, "VoiceAssistantEventResponse"); + dump_field(out, "event_type", static_cast(this->event_type)); for (const auto &it : this->data) { out.append(" data: "); it.dump_to(out); out.append("\n"); } - out.append("}"); } void VoiceAssistantAudio::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantAudio {\n"); + MessageDumpHelper helper(out, "VoiceAssistantAudio"); out.append(" data: "); if (this->data_ptr_ != nullptr) { out.append(format_hex_pretty(this->data_ptr_, this->data_len_)); @@ -3498,938 +1729,341 @@ void VoiceAssistantAudio::dump_to(std::string &out) const { out.append(format_hex_pretty(reinterpret_cast(this->data.data()), this->data.size())); } out.append("\n"); - - out.append(" end: "); - out.append(YESNO(this->end)); - out.append("\n"); - out.append("}"); + dump_field(out, "end", this->end); } void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantTimerEventResponse {\n"); - out.append(" event_type: "); - out.append(proto_enum_to_string(this->event_type)); - out.append("\n"); - - out.append(" timer_id: "); - out.append("'").append(this->timer_id).append("'"); - out.append("\n"); - - out.append(" name: "); - out.append("'").append(this->name).append("'"); - out.append("\n"); - - out.append(" total_seconds: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->total_seconds); - out.append(buffer); - out.append("\n"); - - out.append(" seconds_left: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->seconds_left); - out.append(buffer); - out.append("\n"); - - out.append(" is_active: "); - out.append(YESNO(this->is_active)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "VoiceAssistantTimerEventResponse"); + dump_field(out, "event_type", static_cast(this->event_type)); + dump_field(out, "timer_id", this->timer_id); + dump_field(out, "name", this->name); + dump_field(out, "total_seconds", this->total_seconds); + dump_field(out, "seconds_left", this->seconds_left); + dump_field(out, "is_active", this->is_active); } void VoiceAssistantAnnounceRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantAnnounceRequest {\n"); - out.append(" media_id: "); - out.append("'").append(this->media_id).append("'"); - out.append("\n"); - - out.append(" text: "); - out.append("'").append(this->text).append("'"); - out.append("\n"); - - out.append(" preannounce_media_id: "); - out.append("'").append(this->preannounce_media_id).append("'"); - out.append("\n"); - - out.append(" start_conversation: "); - out.append(YESNO(this->start_conversation)); - out.append("\n"); - out.append("}"); -} -void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantAnnounceFinished {\n"); - out.append(" success: "); - out.append(YESNO(this->success)); - out.append("\n"); - out.append("}"); + MessageDumpHelper helper(out, "VoiceAssistantAnnounceRequest"); + dump_field(out, "media_id", this->media_id); + dump_field(out, "text", this->text); + dump_field(out, "preannounce_media_id", this->preannounce_media_id); + dump_field(out, "start_conversation", this->start_conversation); } +void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { dump_field(out, "success", this->success); } void VoiceAssistantWakeWord::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantWakeWord {\n"); - out.append(" id: "); - append_quoted_string(out, this->id_ref_); - out.append("\n"); - - out.append(" wake_word: "); - append_quoted_string(out, this->wake_word_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "VoiceAssistantWakeWord"); + dump_field(out, "id", this->id_ref_); + dump_field(out, "wake_word", this->wake_word_ref_); for (const auto &it : this->trained_languages) { - out.append(" trained_languages: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "trained_languages", it, 4); } - out.append("}"); } void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { out.append("VoiceAssistantConfigurationRequest {}"); } void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantConfigurationResponse {\n"); + MessageDumpHelper helper(out, "VoiceAssistantConfigurationResponse"); for (const auto &it : this->available_wake_words) { out.append(" available_wake_words: "); it.dump_to(out); out.append("\n"); } - for (const auto &it : this->active_wake_words) { - out.append(" active_wake_words: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "active_wake_words", it, 4); } - - out.append(" max_active_wake_words: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->max_active_wake_words); - out.append(buffer); - out.append("\n"); - out.append("}"); + dump_field(out, "max_active_wake_words", this->max_active_wake_words); } void VoiceAssistantSetConfiguration::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("VoiceAssistantSetConfiguration {\n"); + MessageDumpHelper helper(out, "VoiceAssistantSetConfiguration"); for (const auto &it : this->active_wake_words) { - out.append(" active_wake_words: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "active_wake_words", it, 4); } - out.append("}"); } #endif #ifdef USE_ALARM_CONTROL_PANEL void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesAlarmControlPanelResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesAlarmControlPanelResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" supported_features: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->supported_features); - out.append(buffer); - out.append("\n"); - - out.append(" requires_code: "); - out.append(YESNO(this->requires_code)); - out.append("\n"); - - out.append(" requires_code_to_arm: "); - out.append(YESNO(this->requires_code_to_arm)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "supported_features", this->supported_features); + dump_field(out, "requires_code", this->requires_code); + dump_field(out, "requires_code_to_arm", this->requires_code_to_arm); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void AlarmControlPanelStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("AlarmControlPanelStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append(proto_enum_to_string(this->state)); - out.append("\n"); - + MessageDumpHelper helper(out, "AlarmControlPanelStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", static_cast(this->state)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void AlarmControlPanelCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("AlarmControlPanelCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" command: "); - out.append(proto_enum_to_string(this->command)); - out.append("\n"); - - out.append(" code: "); - out.append("'").append(this->code).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "AlarmControlPanelCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "command", static_cast(this->command)); + dump_field(out, "code", this->code); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_TEXT void ListEntitiesTextResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesTextResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesTextResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" min_length: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->min_length); - out.append(buffer); - out.append("\n"); - - out.append(" max_length: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->max_length); - out.append(buffer); - out.append("\n"); - - out.append(" pattern: "); - append_quoted_string(out, this->pattern_ref_); - out.append("\n"); - - out.append(" mode: "); - out.append(proto_enum_to_string(this->mode)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "min_length", this->min_length); + dump_field(out, "max_length", this->max_length); + dump_field(out, "pattern", this->pattern_ref_); + dump_field(out, "mode", static_cast(this->mode)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void TextStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("TextStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - append_quoted_string(out, this->state_ref_); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - + MessageDumpHelper helper(out, "TextStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state_ref_); + dump_field(out, "missing_state", this->missing_state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void TextCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("TextCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" state: "); - out.append("'").append(this->state).append("'"); - out.append("\n"); - + MessageDumpHelper helper(out, "TextCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "state", this->state); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_DATETIME_DATE void ListEntitiesDateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesDateResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesDateResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void DateStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DateStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - - out.append(" year: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->year); - out.append(buffer); - out.append("\n"); - - out.append(" month: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->month); - out.append(buffer); - out.append("\n"); - - out.append(" day: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->day); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "DateStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "missing_state", this->missing_state); + dump_field(out, "year", this->year); + dump_field(out, "month", this->month); + dump_field(out, "day", this->day); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void DateCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DateCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" year: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->year); - out.append(buffer); - out.append("\n"); - - out.append(" month: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->month); - out.append(buffer); - out.append("\n"); - - out.append(" day: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->day); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "DateCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "year", this->year); + dump_field(out, "month", this->month); + dump_field(out, "day", this->day); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_DATETIME_TIME void ListEntitiesTimeResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesTimeResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesTimeResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void TimeStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("TimeStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - - out.append(" hour: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->hour); - out.append(buffer); - out.append("\n"); - - out.append(" minute: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->minute); - out.append(buffer); - out.append("\n"); - - out.append(" second: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->second); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "TimeStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "missing_state", this->missing_state); + dump_field(out, "hour", this->hour); + dump_field(out, "minute", this->minute); + dump_field(out, "second", this->second); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void TimeCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("TimeCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" hour: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->hour); - out.append(buffer); - out.append("\n"); - - out.append(" minute: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->minute); - out.append(buffer); - out.append("\n"); - - out.append(" second: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->second); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "TimeCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "hour", this->hour); + dump_field(out, "minute", this->minute); + dump_field(out, "second", this->second); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_EVENT void ListEntitiesEventResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesEventResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesEventResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); for (const auto &it : this->event_types) { - out.append(" event_types: "); - append_quoted_string(out, StringRef(it)); - out.append("\n"); + dump_field(out, "event_types", it, 4); } - #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void EventResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("EventResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" event_type: "); - append_quoted_string(out, this->event_type_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "EventResponse"); + dump_field(out, "key", this->key); + dump_field(out, "event_type", this->event_type_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_VALVE void ListEntitiesValveResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesValveResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesValveResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - - out.append(" assumed_state: "); - out.append(YESNO(this->assumed_state)); - out.append("\n"); - - out.append(" supports_position: "); - out.append(YESNO(this->supports_position)); - out.append("\n"); - - out.append(" supports_stop: "); - out.append(YESNO(this->supports_stop)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); + dump_field(out, "assumed_state", this->assumed_state); + dump_field(out, "supports_position", this->supports_position); + dump_field(out, "supports_stop", this->supports_stop); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void ValveStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ValveStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" position: "); - snprintf(buffer, sizeof(buffer), "%g", this->position); - out.append(buffer); - out.append("\n"); - - out.append(" current_operation: "); - out.append(proto_enum_to_string(this->current_operation)); - out.append("\n"); - + MessageDumpHelper helper(out, "ValveStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "position", this->position); + dump_field(out, "current_operation", static_cast(this->current_operation)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void ValveCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ValveCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" has_position: "); - out.append(YESNO(this->has_position)); - out.append("\n"); - - out.append(" position: "); - snprintf(buffer, sizeof(buffer), "%g", this->position); - out.append(buffer); - out.append("\n"); - - out.append(" stop: "); - out.append(YESNO(this->stop)); - out.append("\n"); - + MessageDumpHelper helper(out, "ValveCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "has_position", this->has_position); + dump_field(out, "position", this->position); + dump_field(out, "stop", this->stop); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_DATETIME_DATETIME void ListEntitiesDateTimeResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesDateTimeResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesDateTimeResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void DateTimeStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DateTimeStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - - out.append(" epoch_seconds: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->epoch_seconds); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "DateTimeStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "missing_state", this->missing_state); + dump_field(out, "epoch_seconds", this->epoch_seconds); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void DateTimeCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("DateTimeCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" epoch_seconds: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->epoch_seconds); - out.append(buffer); - out.append("\n"); - + MessageDumpHelper helper(out, "DateTimeCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "epoch_seconds", this->epoch_seconds); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif #ifdef USE_UPDATE void ListEntitiesUpdateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("ListEntitiesUpdateResponse {\n"); - out.append(" object_id: "); - append_quoted_string(out, this->object_id_ref_); - out.append("\n"); - - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" name: "); - append_quoted_string(out, this->name_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "ListEntitiesUpdateResponse"); + dump_field(out, "object_id", this->object_id_ref_); + dump_field(out, "key", this->key); + dump_field(out, "name", this->name_ref_); #ifdef USE_ENTITY_ICON - out.append(" icon: "); - append_quoted_string(out, this->icon_ref_); - out.append("\n"); - + dump_field(out, "icon", this->icon_ref_); #endif - out.append(" disabled_by_default: "); - out.append(YESNO(this->disabled_by_default)); - out.append("\n"); - - out.append(" entity_category: "); - out.append(proto_enum_to_string(this->entity_category)); - out.append("\n"); - - out.append(" device_class: "); - append_quoted_string(out, this->device_class_ref_); - out.append("\n"); - + dump_field(out, "disabled_by_default", this->disabled_by_default); + dump_field(out, "entity_category", static_cast(this->entity_category)); + dump_field(out, "device_class", this->device_class_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void UpdateStateResponse::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("UpdateStateResponse {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" missing_state: "); - out.append(YESNO(this->missing_state)); - out.append("\n"); - - out.append(" in_progress: "); - out.append(YESNO(this->in_progress)); - out.append("\n"); - - out.append(" has_progress: "); - out.append(YESNO(this->has_progress)); - out.append("\n"); - - out.append(" progress: "); - snprintf(buffer, sizeof(buffer), "%g", this->progress); - out.append(buffer); - out.append("\n"); - - out.append(" current_version: "); - append_quoted_string(out, this->current_version_ref_); - out.append("\n"); - - out.append(" latest_version: "); - append_quoted_string(out, this->latest_version_ref_); - out.append("\n"); - - out.append(" title: "); - append_quoted_string(out, this->title_ref_); - out.append("\n"); - - out.append(" release_summary: "); - append_quoted_string(out, this->release_summary_ref_); - out.append("\n"); - - out.append(" release_url: "); - append_quoted_string(out, this->release_url_ref_); - out.append("\n"); - + MessageDumpHelper helper(out, "UpdateStateResponse"); + dump_field(out, "key", this->key); + dump_field(out, "missing_state", this->missing_state); + dump_field(out, "in_progress", this->in_progress); + dump_field(out, "has_progress", this->has_progress); + dump_field(out, "progress", this->progress); + dump_field(out, "current_version", this->current_version_ref_); + dump_field(out, "latest_version", this->latest_version_ref_); + dump_field(out, "title", this->title_ref_); + dump_field(out, "release_summary", this->release_summary_ref_); + dump_field(out, "release_url", this->release_url_ref_); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } void UpdateCommandRequest::dump_to(std::string &out) const { - __attribute__((unused)) char buffer[64]; - out.append("UpdateCommandRequest {\n"); - out.append(" key: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); - out.append(buffer); - out.append("\n"); - - out.append(" command: "); - out.append(proto_enum_to_string(this->command)); - out.append("\n"); - + MessageDumpHelper helper(out, "UpdateCommandRequest"); + dump_field(out, "key", this->key); + dump_field(out, "command", static_cast(this->command)); #ifdef USE_DEVICES - out.append(" device_id: "); - snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); - out.append(buffer); - out.append("\n"); - + dump_field(out, "device_id", this->device_id); #endif - out.append("}"); } #endif diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index fb5db70ae8..635291371e 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -224,12 +224,26 @@ class TypeInfo(ABC): encode_func = None + @classmethod + def can_use_dump_field(cls) -> bool: + """Whether this type can use the dump_field helper functions. + + Returns True for simple types that have dump_field overloads. + Complex types like messages and bytes should return False. + """ + return True + + def dump_field_value(self, value: str) -> str: + """Get the value expression to pass to dump_field. + + Most types just pass the value directly, but some (like enums) need a cast. + """ + return value + @property def dump_content(self) -> str: - o = f'out.append(" {self.name}: ");\n' - o += self.dump(f"this->{self.field_name}") + "\n" - o += 'out.append("\\n");\n' - return o + # Default implementation - subclasses can override if they need special handling + return f'dump_field(out, "{self.name}", {self.dump_field_value(f"this->{self.field_name}")});' @abstractmethod def dump(self, name: str) -> str: @@ -593,6 +607,22 @@ class StringType(TypeInfo): f"}}" ) + @property + def dump_content(self) -> str: + # For SOURCE_CLIENT only, use std::string + if not self._needs_encode: + return f'dump_field(out, "{self.name}", this->{self.field_name});' + + # For SOURCE_SERVER, use StringRef with _ref_ suffix + if not self._needs_decode: + return f'dump_field(out, "{self.name}", this->{self.field_name}_ref_);' + + # For SOURCE_BOTH, we need custom logic + o = f'out.append(" {self.name}: ");\n' + o += self.dump(f"this->{self.field_name}") + "\n" + o += 'out.append("\\n");' + return o + def get_size_calculation(self, name: str, force: bool = False) -> str: # For SOURCE_CLIENT only messages, use the string field directly if not self._needs_encode: @@ -615,6 +645,10 @@ class StringType(TypeInfo): @register_type(11) class MessageType(TypeInfo): + @classmethod + def can_use_dump_field(cls) -> bool: + return False + @property def cpp_type(self) -> str: return self._field.type_name[1:] @@ -651,6 +685,13 @@ class MessageType(TypeInfo): o = f"{name}.dump_to(out);" return o + @property + def dump_content(self) -> str: + o = f'out.append(" {self.name}: ");\n' + o += f"this->{self.field_name}.dump_to(out);\n" + o += 'out.append("\\n");' + return o + def get_size_calculation(self, name: str, force: bool = False) -> str: return self._get_simple_size_calculation(name, force, "add_message_object") @@ -664,6 +705,10 @@ class MessageType(TypeInfo): @register_type(12) class BytesType(TypeInfo): + @classmethod + def can_use_dump_field(cls) -> bool: + return False + cpp_type = "std::string" default_value = "" reference_type = "std::string &" @@ -719,6 +764,13 @@ class BytesType(TypeInfo): f" }}" ) + @property + def dump_content(self) -> str: + o = f'out.append(" {self.name}: ");\n' + o += self.dump(f"this->{self.field_name}") + "\n" + o += 'out.append("\\n");' + return o + def get_size_calculation(self, name: str, force: bool = False) -> str: return f"ProtoSize::add_bytes_field(total_size, {self.calculate_field_id_size()}, this->{self.field_name}_len_);" @@ -729,6 +781,10 @@ class BytesType(TypeInfo): class FixedArrayBytesType(TypeInfo): """Special type for fixed-size byte arrays.""" + @classmethod + def can_use_dump_field(cls) -> bool: + return False + def __init__(self, field: descriptor.FieldDescriptorProto, size: int) -> None: super().__init__(field) self.array_size = size @@ -778,6 +834,13 @@ class FixedArrayBytesType(TypeInfo): o = f"out.append(format_hex_pretty({name}, {name}_len));" return o + @property + def dump_content(self) -> str: + o = f'out.append(" {self.name}: ");\n' + o += f"out.append(format_hex_pretty(this->{self.field_name}, this->{self.field_name}_len));\n" + o += 'out.append("\\n");' + return o + def get_size_calculation(self, name: str, force: bool = False) -> str: # Use the actual length stored in the _len field length_field = f"this->{self.field_name}_len" @@ -850,6 +913,10 @@ class EnumType(TypeInfo): o = f"out.append(proto_enum_to_string<{self.cpp_type}>({name}));" return o + def dump_field_value(self, value: str) -> str: + # Enums need explicit cast for the template + return f"static_cast<{self.cpp_type}>({value})" + def get_size_calculation(self, name: str, force: bool = False) -> str: return self._get_simple_size_calculation( name, force, "add_enum_field", f"static_cast({name})" @@ -947,6 +1014,27 @@ class SInt64Type(TypeInfo): return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint +def _generate_array_dump_content( + ti, field_name: str, name: str, is_bool: bool = False +) -> str: + """Generate dump content for array types (repeated or fixed array). + + Shared helper to avoid code duplication between RepeatedTypeInfo and FixedArrayRepeatedType. + """ + o = f"for (const auto {'' if is_bool else '&'}it : {field_name}) {{\n" + # Check if underlying type can use dump_field + if type(ti).can_use_dump_field(): + # For types that have dump_field overloads, use them with extra indent + o += f' dump_field(out, "{name}", {ti.dump_field_value("it")}, 4);\n' + else: + # For complex types (messages, bytes), use the old pattern + o += f' out.append(" {name}: ");\n' + o += indent(ti.dump("it")) + "\n" + o += ' out.append("\\n");\n' + o += "}" + return o + + class FixedArrayRepeatedType(TypeInfo): """Special type for fixed-size repeated fields using std::array. @@ -1013,12 +1101,9 @@ class FixedArrayRepeatedType(TypeInfo): @property def dump_content(self) -> str: - o = f"for (const auto &it : this->{self.field_name}) {{\n" - o += f' out.append(" {self.name}: ");\n' - o += indent(self._ti.dump("it")) + "\n" - o += ' out.append("\\n");\n' - o += "}\n" - return o + return _generate_array_dump_content( + self._ti, f"this->{self.field_name}", self.name, is_bool=False + ) def dump(self, name: str) -> str: # This is used when dumping the array itself (not its elements) @@ -1144,12 +1229,9 @@ class RepeatedTypeInfo(TypeInfo): @property def dump_content(self) -> str: - o = f"for (const auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n" - o += f' out.append(" {self.name}: ");\n' - o += indent(self._ti.dump("it")) + "\n" - o += ' out.append("\\n");\n' - o += "}\n" - return o + return _generate_array_dump_content( + self._ti, f"this->{self.field_name}", self.name, is_bool=self._ti_is_bool + ) def dump(self, _: str): pass @@ -1644,10 +1726,8 @@ def build_message_type( dump_impl += f" {dump[0]} " else: dump_impl += "\n" - dump_impl += " __attribute__((unused)) char buffer[64];\n" - dump_impl += f' out.append("{desc.name} {{\\n");\n' + dump_impl += f' MessageDumpHelper helper(out, "{desc.name}");\n' dump_impl += indent("\n".join(dump)) + "\n" - dump_impl += ' out.append("}");\n' else: o2 = f'out.append("{desc.name} {{}}");' if len(dump_impl) + len(o2) + 3 < 120: @@ -2004,6 +2084,83 @@ static inline void append_quoted_string(std::string &out, const StringRef &ref) out.append("'"); } +// Common helpers for dump_field functions +static inline void append_field_prefix(std::string &out, const char *field_name, int indent) { + out.append(indent, ' ').append(field_name).append(": "); +} + +static inline void append_with_newline(std::string &out, const char *str) { + out.append(str); + out.append("\\n"); +} + +// RAII helper for message dump formatting +class MessageDumpHelper { + public: + MessageDumpHelper(std::string &out, const char *message_name) : out_(out) { + out_.append(message_name); + out_.append(" {\\n"); + } + ~MessageDumpHelper() { out_.append(" }"); } + + private: + std::string &out_; +}; + +// Helper functions to reduce code duplication in dump methods +static void dump_field(std::string &out, const char *field_name, int32_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%" PRId32, value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, uint32_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%" PRIu32, value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, float value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%g", value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, uint64_t value, int indent = 2) { + char buffer[64]; + append_field_prefix(out, field_name, indent); + snprintf(buffer, 64, "%llu", value); + append_with_newline(out, buffer); +} + +static void dump_field(std::string &out, const char *field_name, bool value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append(YESNO(value)); + out.append("\\n"); +} + +static void dump_field(std::string &out, const char *field_name, const std::string &value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append("'").append(value).append("'"); + out.append("\\n"); +} + +static void dump_field(std::string &out, const char *field_name, StringRef value, int indent = 2) { + append_field_prefix(out, field_name, indent); + append_quoted_string(out, value); + out.append("\\n"); +} + +template +static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) { + append_field_prefix(out, field_name, indent); + out.append(proto_enum_to_string(value)); + out.append("\\n"); +} + """ content += "namespace enums {\n\n" From 99850255f008867ca1cbb18a804af755ee129989 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:21:35 -1000 Subject: [PATCH 18/20] [api] Use emplace_back for TemplatableKeyValuePair construction in HomeAssistant services (#9804) --- esphome/components/api/homeassistant_service.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index 4980c0224c..d91c1ab287 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -36,6 +36,9 @@ template class TemplatableStringValue : public TemplatableValue class TemplatableKeyValuePair { public: + // Keys are always string literals from YAML dictionary keys (e.g., "code", "event") + // and never templatable values or lambdas. Only the value parameter can be a lambda/template. + // Using pass-by-value with std::move allows optimal performance for both lvalues and rvalues. template TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {} std::string key; TemplatableStringValue value; @@ -47,14 +50,15 @@ template class HomeAssistantServiceCallAction : public Action void set_service(T service) { this->service_ = service; } - template void add_data(std::string key, T value) { - this->data_.push_back(TemplatableKeyValuePair(key, value)); - } + // Keys are always string literals from the Python code generation (e.g., cg.add(var.add_data("tag_id", templ))). + // The value parameter can be a lambda/template, but keys are never templatable. + // Using pass-by-value allows the compiler to optimize for both lvalues and rvalues. + template void add_data(std::string key, T value) { this->data_.emplace_back(std::move(key), value); } template void add_data_template(std::string key, T value) { - this->data_template_.push_back(TemplatableKeyValuePair(key, value)); + this->data_template_.emplace_back(std::move(key), value); } template void add_variable(std::string key, T value) { - this->variables_.push_back(TemplatableKeyValuePair(key, value)); + this->variables_.emplace_back(std::move(key), value); } void play(Ts... x) override { From 544cf9b9c082476aa09cd6539ab13bf624ca0003 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:22:42 -1000 Subject: [PATCH 19/20] [core] Fix component state documentation and add state helper method (#9824) --- esphome/core/component.cpp | 28 ++++++++++++---------------- esphome/core/component.h | 11 +++++++---- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index a6a59d30d5..8dcc4496b1 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -151,26 +151,22 @@ void Component::call() { switch (state) { case COMPONENT_STATE_CONSTRUCTION: // State Construction: Call setup and set state to setup - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_SETUP; + this->set_component_state_(COMPONENT_STATE_SETUP); this->call_setup(); break; case COMPONENT_STATE_SETUP: // State setup: Call first loop and set state to loop - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_LOOP; + this->set_component_state_(COMPONENT_STATE_LOOP); this->call_loop(); break; case COMPONENT_STATE_LOOP: // State loop: Call loop this->call_loop(); break; - case COMPONENT_STATE_FAILED: // NOLINT(bugprone-branch-clone) + case COMPONENT_STATE_FAILED: // State failed: Do nothing - break; - case COMPONENT_STATE_LOOP_DONE: // NOLINT(bugprone-branch-clone) + case COMPONENT_STATE_LOOP_DONE: // State loop done: Do nothing, component has finished its work - break; default: break; } @@ -195,25 +191,26 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) { } void Component::mark_failed() { ESP_LOGE(TAG, "%s was marked as failed", this->get_component_source()); - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_FAILED; + this->set_component_state_(COMPONENT_STATE_FAILED); this->status_set_error(); // Also remove from loop since failed components shouldn't loop App.disable_component_loop_(this); } +void Component::set_component_state_(uint8_t state) { + this->component_state_ &= ~COMPONENT_STATE_MASK; + this->component_state_ |= state; +} void Component::disable_loop() { if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { ESP_LOGVV(TAG, "%s loop disabled", this->get_component_source()); - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_LOOP_DONE; + this->set_component_state_(COMPONENT_STATE_LOOP_DONE); App.disable_component_loop_(this); } } void Component::enable_loop() { if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { ESP_LOGVV(TAG, "%s loop enabled", this->get_component_source()); - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_LOOP; + this->set_component_state_(COMPONENT_STATE_LOOP); App.enable_component_loop_(this); } } @@ -233,8 +230,7 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() { void Component::reset_to_construction_state() { if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) { ESP_LOGI(TAG, "%s is being reset to construction state", this->get_component_source()); - this->component_state_ &= ~COMPONENT_STATE_MASK; - this->component_state_ |= COMPONENT_STATE_CONSTRUCTION; + this->set_component_state_(COMPONENT_STATE_CONSTRUCTION); // Clear error status when resetting this->status_clear_error(); } diff --git a/esphome/core/component.h b/esphome/core/component.h index 3734473a02..5f17c1c22a 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -236,6 +236,9 @@ class Component { virtual void call_setup(); virtual void call_dump_config(); + /// Helper to set component state (clears state bits and sets new state) + void set_component_state_(uint8_t state); + /** Set an interval function with a unique name. Empty name means no cancelling possible. * * This will call f every interval ms. Can be cancelled via CancelInterval(). @@ -405,10 +408,10 @@ class Component { const char *component_source_{nullptr}; uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s) /// State of this component - each bit has a purpose: - /// Bits 0-1: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED) - /// Bit 2: STATUS_LED_WARNING - /// Bit 3: STATUS_LED_ERROR - /// Bits 4-7: Unused - reserved for future expansion (50% of the bits are free) + /// Bits 0-2: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED, 0x04=LOOP_DONE) + /// Bit 3: STATUS_LED_WARNING + /// Bit 4: STATUS_LED_ERROR + /// Bits 5-7: Unused - reserved for future expansion uint8_t component_state_{0x00}; volatile bool pending_enable_loop_{false}; ///< ISR-safe flag for enable_loop_soon_any_context }; From ec2e0c50f1cf115d7941846d1c0bb0f54b9879e7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 23 Jul 2025 17:23:45 -1000 Subject: [PATCH 20/20] [bluetooth_proxy] [esp32_ble_tracker] [esp32_ble] Use C++17 nested namespace syntax (#9825) --- esphome/components/bluetooth_proxy/bluetooth_connection.cpp | 6 ++---- esphome/components/bluetooth_proxy/bluetooth_connection.h | 6 ++---- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp | 6 ++---- esphome/components/bluetooth_proxy/bluetooth_proxy.h | 6 ++---- esphome/components/esp32_ble/ble.cpp | 6 ++---- esphome/components/esp32_ble/ble.h | 6 ++---- esphome/components/esp32_ble/ble_advertising.cpp | 6 ++---- esphome/components/esp32_ble/ble_advertising.h | 6 ++---- esphome/components/esp32_ble/ble_event.h | 6 ++---- esphome/components/esp32_ble/ble_scan_result.h | 6 ++---- esphome/components/esp32_ble/ble_uuid.cpp | 6 ++---- esphome/components/esp32_ble/ble_uuid.h | 6 ++---- esphome/components/esp32_ble_tracker/automation.h | 6 ++---- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp | 6 ++---- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h | 6 ++---- 15 files changed, 30 insertions(+), 60 deletions(-) diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 616dba891a..4b84257e27 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -8,8 +8,7 @@ #include "bluetooth_proxy.h" -namespace esphome { -namespace bluetooth_proxy { +namespace esphome::bluetooth_proxy { static const char *const TAG = "bluetooth_proxy.connection"; @@ -422,7 +421,6 @@ esp32_ble_tracker::AdvertisementParserType BluetoothConnection::get_advertisemen return this->proxy_->get_advertisement_parser_type(); } -} // namespace bluetooth_proxy -} // namespace esphome +} // namespace esphome::bluetooth_proxy #endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h index 2673238fba..3fed9d531f 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.h +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -4,8 +4,7 @@ #include "esphome/components/esp32_ble_client/ble_client_base.h" -namespace esphome { -namespace bluetooth_proxy { +namespace esphome::bluetooth_proxy { class BluetoothProxy; @@ -43,7 +42,6 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase { // 1 byte used, 1 byte padding }; -} // namespace bluetooth_proxy -} // namespace esphome +} // namespace esphome::bluetooth_proxy #endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 8a1a2bff6a..de5508c777 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -7,8 +7,7 @@ #ifdef USE_ESP32 -namespace esphome { -namespace bluetooth_proxy { +namespace esphome::bluetooth_proxy { static const char *const TAG = "bluetooth_proxy"; @@ -502,7 +501,6 @@ void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -} // namespace bluetooth_proxy -} // namespace esphome +} // namespace esphome::bluetooth_proxy #endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index b3d9044a2c..d249515fdf 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -18,8 +18,7 @@ #include #include -namespace esphome { -namespace bluetooth_proxy { +namespace esphome::bluetooth_proxy { static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; static const int DONE_SENDING_SERVICES = -2; @@ -158,7 +157,6 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -} // namespace bluetooth_proxy -} // namespace esphome +} // namespace esphome::bluetooth_proxy #endif // USE_ESP32 diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 8b0cf4da98..35c48a711a 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -19,8 +19,7 @@ #include #endif -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble"; @@ -538,7 +537,6 @@ uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) { ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 2c5697df82..543b2f26a3 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -21,8 +21,7 @@ #include #include -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { // Maximum number of BLE scan results to buffer // Sized to handle bursts of advertisements while allowing for processing delays @@ -191,7 +190,6 @@ template class BLEDisableAction : public Action { void play(Ts... x) override { global_ble->disable(); } }; -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 8d43b5af33..6a0d677aa7 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -8,8 +8,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble.advertising"; @@ -160,7 +159,6 @@ void BLEAdvertising::register_raw_advertisement_callback(std::functionraw_advertisements_callbacks_.push_back(std::move(callback)); } -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index 0b2142115d..2254251486 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -10,8 +10,7 @@ #include #include -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { using raw_adv_data_t = struct { uint8_t *data; @@ -55,7 +54,6 @@ class BLEAdvertising { int8_t current_adv_index_{-1}; // -1 means standard scan response }; -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index 9268c710f3..884fc9ba65 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -11,8 +11,7 @@ #include "ble_scan_result.h" -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { // Compile-time verification that ESP-IDF scan complete events only contain a status field // This ensures our reinterpret_cast in ble.cpp is safe @@ -395,7 +394,6 @@ static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScan // BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding) -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_scan_result.h b/esphome/components/esp32_ble/ble_scan_result.h index 49b0d5523d..980b39b0b2 100644 --- a/esphome/components/esp32_ble/ble_scan_result.h +++ b/esphome/components/esp32_ble/ble_scan_result.h @@ -4,8 +4,7 @@ #include -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { // Structure for BLE scan results - only fields we actually use struct __attribute__((packed)) BLEScanResult { @@ -18,7 +17,6 @@ struct __attribute__((packed)) BLEScanResult { uint8_t search_evt; }; // ~73 bytes vs ~400 bytes for full esp_ble_gap_cb_param_t -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index aa1edd96b2..fc6981acd3 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -7,8 +7,7 @@ #include #include "esphome/core/log.h" -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { static const char *const TAG = "esp32_ble"; @@ -189,7 +188,6 @@ std::string ESPBTUUID::to_string() const { return ""; } -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 06f84d4da7..150ca359d3 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -8,8 +8,7 @@ #include #include -namespace esphome { -namespace esp32_ble { +namespace esphome::esp32_ble { class ESPBTUUID { public: @@ -41,7 +40,6 @@ class ESPBTUUID { esp_bt_uuid_t uuid_; }; -} // namespace esp32_ble -} // namespace esphome +} // namespace esphome::esp32_ble #endif diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index ef677922e3..c0e6eee138 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -5,8 +5,7 @@ #ifdef USE_ESP32 -namespace esphome { -namespace esp32_ble_tracker { +namespace esphome::esp32_ble_tracker { #ifdef USE_ESP32_BLE_DEVICE class ESPBTAdvertiseTrigger : public Trigger, public ESPBTDeviceListener { public: @@ -108,7 +107,6 @@ template class ESP32BLEStopScanAction : public Action, pu void play(Ts... x) override { this->parent_->stop_scan(); } }; -} // namespace esp32_ble_tracker -} // namespace esphome +} // namespace esphome::esp32_ble_tracker #endif diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 96003073d7..e0029ad15b 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -35,8 +35,7 @@ // bt_trace.h #undef TAG -namespace esphome { -namespace esp32_ble_tracker { +namespace esphome::esp32_ble_tracker { static const char *const TAG = "esp32_ble_tracker"; @@ -882,7 +881,6 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const { } #endif // USE_ESP32_BLE_DEVICE -} // namespace esp32_ble_tracker -} // namespace esphome +} // namespace esphome::esp32_ble_tracker #endif // USE_ESP32 diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index e10f4551e8..e1119c0e18 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -22,8 +22,7 @@ #include "esphome/components/esp32_ble/ble.h" #include "esphome/components/esp32_ble/ble_uuid.h" -namespace esphome { -namespace esp32_ble_tracker { +namespace esphome::esp32_ble_tracker { using namespace esp32_ble; @@ -321,7 +320,6 @@ class ESP32BLETracker : public Component, // NOLINTNEXTLINE extern ESP32BLETracker *global_esp32_ble_tracker; -} // namespace esp32_ble_tracker -} // namespace esphome +} // namespace esphome::esp32_ble_tracker #endif