From bd75f0dfea71568698c2f3cf28f1686f54ceb43c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Jul 2025 11:11:42 -1000 Subject: [PATCH 01/90] Fix another race in the string lifetime scheduler test (#9399) --- .../string_lifetime_component.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/integration/fixtures/external_components/scheduler_string_lifetime_component/string_lifetime_component.cpp b/tests/integration/fixtures/external_components/scheduler_string_lifetime_component/string_lifetime_component.cpp index ea386881b2..8c3f665f19 100644 --- a/tests/integration/fixtures/external_components/scheduler_string_lifetime_component/string_lifetime_component.cpp +++ b/tests/integration/fixtures/external_components/scheduler_string_lifetime_component/string_lifetime_component.cpp @@ -23,19 +23,6 @@ void SchedulerStringLifetimeComponent::run_string_lifetime_test() { test_vector_reallocation(); test_string_move_semantics(); test_lambda_capture_lifetime(); - - // Schedule final check - this->set_timeout("final_check", 200, [this]() { - ESP_LOGI(TAG, "Tests passed: %d", this->tests_passed_); - ESP_LOGI(TAG, "Tests failed: %d", this->tests_failed_); - - if (this->tests_failed_ == 0) { - ESP_LOGI(TAG, "SUCCESS: All string lifetime tests passed!"); - } else { - ESP_LOGE(TAG, "FAILURE: %d string lifetime tests failed!", this->tests_failed_); - } - ESP_LOGI(TAG, "String lifetime tests complete"); - }); } void SchedulerStringLifetimeComponent::run_test1() { @@ -69,7 +56,6 @@ void SchedulerStringLifetimeComponent::run_test5() { } void SchedulerStringLifetimeComponent::run_final_check() { - ESP_LOGI(TAG, "String lifetime tests complete"); ESP_LOGI(TAG, "Tests passed: %d", this->tests_passed_); ESP_LOGI(TAG, "Tests failed: %d", this->tests_failed_); @@ -78,6 +64,7 @@ void SchedulerStringLifetimeComponent::run_final_check() { } else { ESP_LOGE(TAG, "FAILURE: %d string lifetime tests failed!", this->tests_failed_); } + ESP_LOGI(TAG, "String lifetime tests complete"); } void SchedulerStringLifetimeComponent::test_temporary_string_lifetime() { From 92d03dd196f087ae0bfc9b5cb9e8355da3d610ad Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 9 Jul 2025 20:18:01 -0400 Subject: [PATCH 02/90] [esp32_touch] Fix touch v1 (#9414) --- .../components/esp32_touch/esp32_touch_v1.cpp | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32_touch/esp32_touch_v1.cpp b/esphome/components/esp32_touch/esp32_touch_v1.cpp index 6f05610ed6..c3d43c6bbf 100644 --- a/esphome/components/esp32_touch/esp32_touch_v1.cpp +++ b/esphome/components/esp32_touch/esp32_touch_v1.cpp @@ -109,6 +109,7 @@ void ESP32TouchComponent::loop() { // Only publish if state changed - this filters out repeated events if (new_state != child->last_state_) { + child->initial_state_published_ = true; child->last_state_ = new_state; child->publish_state(new_state); // Original ESP32: ISR only fires when touched, release is detected by timeout @@ -175,6 +176,9 @@ void ESP32TouchComponent::on_shutdown() { void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) { ESP32TouchComponent *component = static_cast(arg); + uint32_t mask = 0; + touch_ll_read_trigger_status_mask(&mask); + touch_ll_clear_trigger_status_mask(); touch_pad_clear_status(); // INTERRUPT BEHAVIOR: On ESP32 v1 hardware, the interrupt fires when ANY configured @@ -184,6 +188,11 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) { // as any pad remains touched. This allows us to detect both new touches and // continued touches, but releases must be detected by timeout in the main loop. + // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2! + // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE + // Therefore: touched = (value < threshold) + // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold) + // Process all configured pads to check their current state // Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt, // so we must scan all configured pads to find which ones were touched @@ -201,19 +210,12 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) { value = touch_ll_read_raw_data(pad); } - // Skip pads with 0 value - they haven't been measured in this cycle - // This is important: not all pads are measured every interrupt cycle, - // only those that the hardware has updated - if (value == 0) { + // Skip pads that aren’t in the trigger mask + bool is_touched = (mask >> pad) & 1; + if (!is_touched) { continue; } - // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2! - // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE - // Therefore: touched = (value < threshold) - // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold) - bool is_touched = value < child->get_threshold(); - // Always send the current state - the main loop will filter for changes // We send both touched and untouched states because the ISR doesn't // track previous state (to keep ISR fast and simple) From a5055094d0dc252790d5ed124ab7fd223cf52d10 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 9 Jul 2025 22:49:36 -0400 Subject: [PATCH 03/90] [esp32] Set lib_compat_mode to strict (#9408) --- esphome/components/esp32/__init__.py | 1 + esphome/components/esp8266/__init__.py | 1 + esphome/components/host/__init__.py | 1 + esphome/components/libretiny/__init__.py | 1 + esphome/components/rp2040/__init__.py | 1 + platformio.ini | 1 + 6 files changed, 6 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 8408f902ef..fdc469e419 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -707,6 +707,7 @@ async def to_code(config): cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[config[CONF_VARIANT]]) cg.add_platformio_option("lib_ldf_mode", "off") + cg.add_platformio_option("lib_compat_mode", "strict") framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 81daad8c56..01b20bdcb1 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -180,6 +180,7 @@ async def to_code(config): cg.add(esp8266_ns.setup_preferences()) cg.add_platformio_option("lib_ldf_mode", "off") + cg.add_platformio_option("lib_compat_mode", "strict") cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_ESP8266") diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index d3dbcba6ed..a67d73fbb7 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -45,3 +45,4 @@ async def to_code(config): cg.add_define("ESPHOME_BOARD", "host") cg.add_platformio_option("platform", "platformio/native") cg.add_platformio_option("lib_ldf_mode", "off") + cg.add_platformio_option("lib_compat_mode", "strict") diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 149e5d1179..d5a5dd3ee3 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -268,6 +268,7 @@ async def component_to_code(config): # disable library compatibility checks cg.add_platformio_option("lib_ldf_mode", "off") + cg.add_platformio_option("lib_compat_mode", "strict") # include in every file cg.add_platformio_option("build_src_flags", "-include Arduino.h") # dummy version code diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index ecbeb83bb4..11ed97831e 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -165,6 +165,7 @@ async def to_code(config): # Allow LDF to properly discover dependency including those in preprocessor # conditionals cg.add_platformio_option("lib_ldf_mode", "chain+") + cg.add_platformio_option("lib_compat_mode", "strict") cg.add_platformio_option("board", config[CONF_BOARD]) cg.add_build_flag("-DUSE_RP2040") cg.set_cpp_standard("gnu++20") diff --git a/platformio.ini b/platformio.ini index 0d67e23222..7f10f0f51f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -61,6 +61,7 @@ src_filter = +<../tests/dummy_main.cpp> +<../.temp/all-include.cpp> lib_ldf_mode = off +lib_compat_mode = strict ; This are common settings for all Arduino-framework based environments. [common:arduino] From 5ba493acc389c703e94ade593af83a6e3794118f Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Thu, 10 Jul 2025 05:01:21 +0200 Subject: [PATCH 04/90] debug: bufferoverflow mitigation in DebugComponent::on_shutdown() (#9422) --- esphome/components/debug/debug_esp32.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index e48a4941b3..37990aeec5 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -53,6 +53,7 @@ void DebugComponent::on_shutdown() { auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name())); if (component != nullptr) { strncpy(buffer, component->get_component_source(), REBOOT_MAX_LEN - 1); + buffer[REBOOT_MAX_LEN - 1] = '\0'; } ESP_LOGD(TAG, "Storing reboot source: %s", buffer); pref.save(&buffer); @@ -68,6 +69,7 @@ std::string DebugComponent::get_reset_reason_() { auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name())); char buffer[REBOOT_MAX_LEN]{}; if (pref.load(&buffer)) { + buffer[REBOOT_MAX_LEN - 1] = '\0'; reset_reason = "Reboot request from " + std::string(buffer); } } From 23dd2d648e3d77aaebd6d7cec8e17ef88ac978ab Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 9 Jul 2025 22:34:01 -1000 Subject: [PATCH 05/90] Exclude internal entities from name uniqueness validation (#9410) --- esphome/core/entity_helpers.py | 6 +++ tests/unit_tests/core/test_entity_helpers.py | 57 +++++++++++++++----- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index a3244856a2..5ad16ac76c 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -187,6 +187,12 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy # No name to validate return config + # Skip validation for internal entities + # Internal entities are not exposed to Home Assistant and don't use the hash-based + # entity state tracking system, so name collisions don't matter for them + if config.get(CONF_INTERNAL, False): + return config + # Get the entity name entity_name = config[CONF_NAME] diff --git a/tests/unit_tests/core/test_entity_helpers.py b/tests/unit_tests/core/test_entity_helpers.py index 0dcdd84507..4f256ffb33 100644 --- a/tests/unit_tests/core/test_entity_helpers.py +++ b/tests/unit_tests/core/test_entity_helpers.py @@ -8,9 +8,19 @@ from typing import Any import pytest from esphome.config_validation import Invalid -from esphome.const import CONF_DEVICE_ID, CONF_DISABLED_BY_DEFAULT, CONF_ICON, CONF_NAME +from esphome.const import ( + CONF_DEVICE_ID, + CONF_DISABLED_BY_DEFAULT, + CONF_ICON, + CONF_INTERNAL, + CONF_NAME, +) from esphome.core import CORE, ID, entity_helpers -from esphome.core.entity_helpers import get_base_entity_object_id, setup_entity +from esphome.core.entity_helpers import ( + entity_duplicate_validator, + get_base_entity_object_id, + setup_entity, +) from esphome.cpp_generator import MockObj from esphome.helpers import sanitize, snake_case @@ -493,11 +503,6 @@ async def test_setup_entity_disabled_by_default( def test_entity_duplicate_validator() -> None: """Test the entity_duplicate_validator function.""" - from esphome.core.entity_helpers import entity_duplicate_validator - - # Reset CORE unique_ids for clean test - CORE.unique_ids.clear() - # Create validator for sensor platform validator = entity_duplicate_validator("sensor") @@ -523,11 +528,6 @@ def test_entity_duplicate_validator() -> None: def test_entity_duplicate_validator_with_devices() -> None: """Test entity_duplicate_validator with devices.""" - from esphome.core.entity_helpers import entity_duplicate_validator - - # Reset CORE unique_ids for clean test - CORE.unique_ids.clear() - # Create validator for sensor platform validator = entity_duplicate_validator("sensor") @@ -605,3 +605,36 @@ def test_entity_different_platforms_yaml_validation( ) # This should succeed assert result is not None + + +def test_entity_duplicate_validator_internal_entities() -> None: + """Test that internal entities are excluded from duplicate name validation.""" + # Create validator for sensor platform + validator = entity_duplicate_validator("sensor") + + # First entity should pass + config1 = {CONF_NAME: "Temperature"} + validated1 = validator(config1) + assert validated1 == config1 + assert ("sensor", "temperature") in CORE.unique_ids + + # Internal entity with same name should pass (not added to unique_ids) + config2 = {CONF_NAME: "Temperature", CONF_INTERNAL: True} + validated2 = validator(config2) + assert validated2 == config2 + # Internal entity should not be added to unique_ids + assert len([k for k in CORE.unique_ids if k == ("sensor", "temperature")]) == 1 + + # Another internal entity with same name should also pass + config3 = {CONF_NAME: "Temperature", CONF_INTERNAL: True} + validated3 = validator(config3) + assert validated3 == config3 + # Still only one entry in unique_ids (from the non-internal entity) + assert len([k for k in CORE.unique_ids if k == ("sensor", "temperature")]) == 1 + + # Non-internal entity with same name should fail + config4 = {CONF_NAME: "Temperature"} + with pytest.raises( + Invalid, match=r"Duplicate sensor entity with name 'Temperature' found" + ): + validator(config4) From 0ef5f1fd653dc5797d256af9cba3e18292e379ea Mon Sep 17 00:00:00 2001 From: Adam Liddell Date: Thu, 10 Jul 2025 09:34:43 +0100 Subject: [PATCH 06/90] Handle ESP32 chunked MQTT messages missing topic on non-first chunks, causing panic (#5786) Co-authored-by: Samuel Sieb --- esphome/components/mqtt/mqtt_backend_esp32.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index a096408aa5..623206a0cd 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -153,11 +153,15 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) { case MQTT_EVENT_DATA: { static std::string topic; if (!event.topic.empty()) { + // When a single message arrives as multiple chunks, the topic will be empty + // on any but the first message, leading to event.topic being an empty string. + // To ensure handlers get the correct topic, cache the last seen topic to + // simulate always receiving the topic from underlying library topic = event.topic; } ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str()); - this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(), - event.current_data_offset, event.total_data_len); + this->on_message_.call(topic.c_str(), event.data.data(), event.data.size(), event.current_data_offset, + event.total_data_len); } break; case MQTT_EVENT_ERROR: ESP_LOGE(TAG, "MQTT_EVENT_ERROR"); From fa262673e413d765708034b6a55c62d584326d2e Mon Sep 17 00:00:00 2001 From: DT-art1 <81360462+DT-art1@users.noreply.github.com> Date: Thu, 10 Jul 2025 10:35:24 +0200 Subject: [PATCH 07/90] Replace remaining instances of USE_ESP32_CAMERA with USE_CAMERA (#9401) --- esphome/components/api/api_connection.cpp | 2 +- esphome/components/esp32_camera/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 779784e787..537d75467f 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1920,7 +1920,7 @@ uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { case ListEntitiesClimateResponse::MESSAGE_TYPE: return ListEntitiesClimateResponse::ESTIMATED_SIZE; #endif -#ifdef USE_ESP32_CAMERA +#ifdef USE_CAMERA case ListEntitiesCameraResponse::MESSAGE_TYPE: return ListEntitiesCameraResponse::ESTIMATED_SIZE; #endif diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 138f318a5d..6e36f7d5a7 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -308,7 +308,7 @@ async def to_code(config): cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT])) cg.add(var.set_frame_size(config[CONF_RESOLUTION])) - cg.add_define("USE_ESP32_CAMERA") + cg.add_define("USE_CAMERA") if CORE.using_esp_idf: add_idf_component(name="espressif/esp32-camera", ref="2.0.15") From 904c7b8a3a3d4b151d5de5d0f124557b23532d18 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 08:33:18 -1000 Subject: [PATCH 08/90] Sync api.proto from aioesphomeapi (#9393) --- esphome/components/api/api.proto | 38 ++++++ esphome/components/api/api_pb2.cpp | 148 +++++++++++++++++++++++- esphome/components/api/api_pb2.h | 109 +++++++++-------- esphome/components/api/api_pb2_dump.cpp | 95 +++++++++++++++ 4 files changed, 332 insertions(+), 58 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index c3795bb796..c35e603628 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -374,6 +374,7 @@ message CoverCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_COVER"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; @@ -387,6 +388,7 @@ message CoverCommandRequest { bool has_tilt = 6; float tilt = 7; bool stop = 8; + uint32 device_id = 9; } // ==================== FAN ==================== @@ -441,6 +443,7 @@ message FanCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_FAN"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool has_state = 2; @@ -455,6 +458,7 @@ message FanCommandRequest { int32 speed_level = 11; bool has_preset_mode = 12; string preset_mode = 13; + uint32 device_id = 14; } // ==================== LIGHT ==================== @@ -523,6 +527,7 @@ message LightCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_LIGHT"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool has_state = 2; @@ -551,6 +556,7 @@ message LightCommandRequest { uint32 flash_length = 17; bool has_effect = 18; string effect = 19; + uint32 device_id = 28; } // ==================== SENSOR ==================== @@ -640,9 +646,11 @@ message SwitchCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_SWITCH"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool state = 2; + uint32 device_id = 3; } // ==================== TEXT SENSOR ==================== @@ -850,12 +858,14 @@ message ListEntitiesCameraResponse { message CameraImageResponse { option (id) = 44; + option (base_class) = "StateResponseProtoMessage"; option (source) = SOURCE_SERVER; option (ifdef) = "USE_CAMERA"; fixed32 key = 1; bytes data = 2; bool done = 3; + uint32 device_id = 4; } message CameraImageRequest { option (id) = 45; @@ -980,6 +990,7 @@ message ClimateCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_CLIMATE"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool has_mode = 2; @@ -1005,6 +1016,7 @@ message ClimateCommandRequest { string custom_preset = 21; bool has_target_humidity = 22; float target_humidity = 23; + uint32 device_id = 24; } // ==================== NUMBER ==================== @@ -1054,9 +1066,11 @@ message NumberCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_NUMBER"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; float state = 2; + uint32 device_id = 3; } // ==================== SELECT ==================== @@ -1096,9 +1110,11 @@ message SelectCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_SELECT"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; string state = 2; + uint32 device_id = 3; } // ==================== SIREN ==================== @@ -1137,6 +1153,7 @@ message SirenCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_SIREN"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool has_state = 2; @@ -1147,6 +1164,7 @@ message SirenCommandRequest { uint32 duration = 7; bool has_volume = 8; float volume = 9; + uint32 device_id = 10; } // ==================== LOCK ==================== @@ -1201,12 +1219,14 @@ message LockCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_LOCK"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; LockCommand command = 2; // Not yet implemented: bool has_code = 3; string code = 4; + uint32 device_id = 5; } // ==================== BUTTON ==================== @@ -1232,8 +1252,10 @@ message ButtonCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_BUTTON"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; + uint32 device_id = 2; } // ==================== MEDIA PLAYER ==================== @@ -1301,6 +1323,7 @@ message MediaPlayerCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_MEDIA_PLAYER"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; @@ -1315,6 +1338,7 @@ message MediaPlayerCommandRequest { bool has_announcement = 8; bool announcement = 9; + uint32 device_id = 10; } // ==================== BLUETOOTH ==================== @@ -1843,9 +1867,11 @@ message AlarmControlPanelCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_ALARM_CONTROL_PANEL"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; AlarmControlPanelStateCommand command = 2; string code = 3; + uint32 device_id = 4; } // ===================== TEXT ===================== @@ -1892,9 +1918,11 @@ message TextCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_TEXT"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; string state = 2; + uint32 device_id = 3; } @@ -1936,11 +1964,13 @@ message DateCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_DATETIME_DATE"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; uint32 year = 2; uint32 month = 3; uint32 day = 4; + uint32 device_id = 5; } // ==================== DATETIME TIME ==================== @@ -1981,11 +2011,13 @@ message TimeCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_DATETIME_TIME"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; uint32 hour = 2; uint32 minute = 3; uint32 second = 4; + uint32 device_id = 5; } // ==================== EVENT ==================== @@ -2065,11 +2097,13 @@ message ValveCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_VALVE"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; bool has_position = 2; float position = 3; bool stop = 4; + uint32 device_id = 5; } // ==================== DATETIME DATETIME ==================== @@ -2108,9 +2142,11 @@ message DateTimeCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_DATETIME_DATETIME"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; fixed32 epoch_seconds = 2; + uint32 device_id = 3; } // ==================== UPDATE ==================== @@ -2160,7 +2196,9 @@ message UpdateCommandRequest { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_UPDATE"; option (no_delay) = true; + option (base_class) = "CommandProtoMessage"; fixed32 key = 1; UpdateCommand command = 2; + uint32 device_id = 3; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 3505ec758d..af82299f53 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -623,6 +623,10 @@ bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->stop = value.as_bool(); return true; } + case 9: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -654,6 +658,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(6, this->has_tilt); buffer.encode_float(7, this->tilt); buffer.encode_bool(8, this->stop); + buffer.encode_uint32(9, this->device_id); } void CoverCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -664,6 +669,7 @@ void CoverCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->has_tilt, false); ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f, false); ProtoSize::add_bool_field(total_size, 1, this->stop, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_FAN @@ -889,6 +895,10 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->has_preset_mode = value.as_bool(); return true; } + case 14: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -927,6 +937,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(11, this->speed_level); buffer.encode_bool(12, this->has_preset_mode); buffer.encode_string(13, this->preset_mode); + buffer.encode_uint32(14, this->device_id); } void FanCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -942,6 +953,7 @@ void FanCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_int32_field(total_size, 1, this->speed_level, false); ProtoSize::add_bool_field(total_size, 1, this->has_preset_mode, false); ProtoSize::add_string_field(total_size, 1, this->preset_mode, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_LIGHT @@ -1247,6 +1259,10 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->has_effect = value.as_bool(); return true; } + case 28: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -1335,6 +1351,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(17, this->flash_length); buffer.encode_bool(18, this->has_effect); buffer.encode_string(19, this->effect); + buffer.encode_uint32(28, this->device_id); } void LightCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -1364,6 +1381,7 @@ void LightCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 2, this->flash_length, false); ProtoSize::add_bool_field(total_size, 2, this->has_effect, false); ProtoSize::add_string_field(total_size, 2, this->effect, false); + ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); } #endif #ifdef USE_SENSOR @@ -1637,6 +1655,10 @@ bool SwitchCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->state = value.as_bool(); return true; } + case 3: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -1654,10 +1676,12 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); + buffer.encode_uint32(3, this->device_id); } void SwitchCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_bool_field(total_size, 1, this->state, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_TEXT_SENSOR @@ -2293,6 +2317,10 @@ bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { this->done = value.as_bool(); return true; } + case 4: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -2321,11 +2349,13 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bytes(2, reinterpret_cast(this->data.data()), this->data.size()); buffer.encode_bool(3, this->done); + buffer.encode_uint32(4, this->device_id); } void CameraImageResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_string_field(total_size, 1, this->data, false); ProtoSize::add_bool_field(total_size, 1, this->done, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2749,6 +2779,10 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) this->has_target_humidity = value.as_bool(); return true; } + case 24: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -2817,6 +2851,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(21, this->custom_preset); buffer.encode_bool(22, this->has_target_humidity); buffer.encode_float(23, this->target_humidity); + buffer.encode_uint32(24, this->device_id); } void ClimateCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -2842,6 +2877,7 @@ void ClimateCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 2, this->custom_preset, false); ProtoSize::add_bool_field(total_size, 2, this->has_target_humidity, false); ProtoSize::add_fixed_field<4>(total_size, 2, this->target_humidity != 0.0f, false); + ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); } #endif #ifdef USE_NUMBER @@ -2991,6 +3027,16 @@ void NumberStateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } +bool NumberCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->device_id = value.as_uint32(); + return true; + } + default: + return false; + } +} bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { switch (field_id) { case 1: { @@ -3008,10 +3054,12 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_float(2, this->state); + buffer.encode_uint32(3, this->device_id); } void NumberCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_SELECT @@ -3143,6 +3191,16 @@ void SelectStateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } +bool SelectCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->device_id = value.as_uint32(); + return true; + } + default: + return false; + } +} bool SelectCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: { @@ -3166,10 +3224,12 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->state); + buffer.encode_uint32(3, this->device_id); } void SelectCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_string_field(total_size, 1, this->state, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_SIREN @@ -3327,6 +3387,10 @@ bool SirenCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->has_volume = value.as_bool(); return true; } + case 10: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -3365,6 +3429,7 @@ void SirenCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(7, this->duration); buffer.encode_bool(8, this->has_volume); buffer.encode_float(9, this->volume); + buffer.encode_uint32(10, this->device_id); } void SirenCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -3376,6 +3441,7 @@ void SirenCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 1, this->duration, false); ProtoSize::add_bool_field(total_size, 1, this->has_volume, false); ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_LOCK @@ -3517,6 +3583,10 @@ bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->has_code = value.as_bool(); return true; } + case 5: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -3546,12 +3616,14 @@ void LockCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(2, this->command); buffer.encode_bool(3, this->has_code); buffer.encode_string(4, this->code); + buffer.encode_uint32(5, this->device_id); } void LockCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); ProtoSize::add_bool_field(total_size, 1, this->has_code, false); ProtoSize::add_string_field(total_size, 1, this->code, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_BUTTON @@ -3631,6 +3703,16 @@ void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } +bool ButtonCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->device_id = value.as_uint32(); + return true; + } + default: + return false; + } +} bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { switch (field_id) { case 1: { @@ -3641,9 +3723,13 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); } +void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_uint32(2, this->device_id); +} void ButtonCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_MEDIA_PLAYER @@ -3849,6 +3935,10 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val this->announcement = value.as_bool(); return true; } + case 10: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -3887,6 +3977,7 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(7, this->media_url); buffer.encode_bool(8, this->has_announcement); buffer.encode_bool(9, this->announcement); + buffer.encode_uint32(10, this->device_id); } void MediaPlayerCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); @@ -3898,6 +3989,7 @@ void MediaPlayerCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->media_url, false); ProtoSize::add_bool_field(total_size, 1, this->has_announcement, false); ProtoSize::add_bool_field(total_size, 1, this->announcement, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_BLUETOOTH_PROXY @@ -5311,6 +5403,10 @@ bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarI this->command = value.as_enum(); return true; } + case 4: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -5339,11 +5435,13 @@ void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_enum(2, this->command); buffer.encode_string(3, this->code); + buffer.encode_uint32(4, this->device_id); } void AlarmControlPanelCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); ProtoSize::add_string_field(total_size, 1, this->code, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_TEXT @@ -5487,6 +5585,16 @@ void TextStateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } +bool TextCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->device_id = value.as_uint32(); + return true; + } + default: + return false; + } +} bool TextCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: { @@ -5510,10 +5618,12 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void TextCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->state); + buffer.encode_uint32(3, this->device_id); } void TextCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_string_field(total_size, 1, this->state, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_DATETIME_DATE @@ -5653,6 +5763,10 @@ bool DateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->day = value.as_uint32(); return true; } + case 5: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -5672,12 +5786,14 @@ void DateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->year); buffer.encode_uint32(3, this->month); buffer.encode_uint32(4, this->day); + buffer.encode_uint32(5, this->device_id); } void DateCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_uint32_field(total_size, 1, this->year, false); ProtoSize::add_uint32_field(total_size, 1, this->month, false); ProtoSize::add_uint32_field(total_size, 1, this->day, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_DATETIME_TIME @@ -5817,6 +5933,10 @@ bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->second = value.as_uint32(); return true; } + case 5: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -5836,12 +5956,14 @@ void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->hour); buffer.encode_uint32(3, this->minute); buffer.encode_uint32(4, this->second); + buffer.encode_uint32(5, this->device_id); } void TimeCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_uint32_field(total_size, 1, this->hour, false); ProtoSize::add_uint32_field(total_size, 1, this->minute, false); ProtoSize::add_uint32_field(total_size, 1, this->second, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_EVENT @@ -6119,6 +6241,10 @@ bool ValveCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->stop = value.as_bool(); return true; } + case 5: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -6142,12 +6268,14 @@ void ValveCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->has_position); buffer.encode_float(3, this->position); buffer.encode_bool(4, this->stop); + buffer.encode_uint32(5, this->device_id); } void ValveCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_bool_field(total_size, 1, this->has_position, false); ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f, false); ProtoSize::add_bool_field(total_size, 1, this->stop, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_DATETIME_DATETIME @@ -6261,6 +6389,16 @@ void DateTimeStateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0, false); ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } +bool DateTimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->device_id = value.as_uint32(); + return true; + } + default: + return false; + } +} bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { switch (field_id) { case 1: { @@ -6278,10 +6416,12 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void DateTimeCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_fixed32(2, this->epoch_seconds); + buffer.encode_uint32(3, this->device_id); } void DateTimeCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif #ifdef USE_UPDATE @@ -6455,6 +6595,10 @@ bool UpdateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->command = value.as_enum(); return true; } + case 3: { + this->device_id = value.as_uint32(); + return true; + } default: return false; } @@ -6472,10 +6616,12 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_enum(2, this->command); + buffer.encode_uint32(3, this->device_id); } void UpdateCommandRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 3bfc5f1cf4..029f22dfc2 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -307,6 +307,15 @@ class StateResponseProtoMessage : public ProtoMessage { protected: }; + +class CommandProtoMessage : public ProtoMessage { + public: + ~CommandProtoMessage() override = default; + uint32_t key{0}; + uint32_t device_id{0}; + + protected: +}; class HelloRequest : public ProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 1; @@ -640,14 +649,13 @@ class CoverStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class CoverCommandRequest : public ProtoMessage { +class CoverCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 30; - static constexpr uint16_t ESTIMATED_SIZE = 25; + static constexpr uint16_t ESTIMATED_SIZE = 29; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "cover_command_request"; } #endif - uint32_t key{0}; bool has_legacy_command{false}; enums::LegacyCoverCommand legacy_command{}; bool has_position{false}; @@ -714,14 +722,13 @@ class FanStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class FanCommandRequest : public ProtoMessage { +class FanCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 31; - static constexpr uint16_t ESTIMATED_SIZE = 38; + static constexpr uint16_t ESTIMATED_SIZE = 42; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "fan_command_request"; } #endif - uint32_t key{0}; bool has_state{false}; bool state{false}; bool has_speed{false}; @@ -803,14 +810,13 @@ class LightStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LightCommandRequest : public ProtoMessage { +class LightCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 32; - static constexpr uint16_t ESTIMATED_SIZE = 107; + static constexpr uint16_t ESTIMATED_SIZE = 112; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "light_command_request"; } #endif - uint32_t key{0}; bool has_state{false}; bool state{false}; bool has_brightness{false}; @@ -933,14 +939,13 @@ class SwitchStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SwitchCommandRequest : public ProtoMessage { +class SwitchCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 33; - static constexpr uint16_t ESTIMATED_SIZE = 7; + static constexpr uint16_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "switch_command_request"; } #endif - uint32_t key{0}; bool state{false}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1292,14 +1297,13 @@ class ListEntitiesCameraResponse : public InfoResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class CameraImageResponse : public ProtoMessage { +class CameraImageResponse : public StateResponseProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 44; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint16_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "camera_image_response"; } #endif - uint32_t key{0}; std::string data{}; bool done{false}; void encode(ProtoWriteBuffer buffer) const override; @@ -1401,14 +1405,13 @@ class ClimateStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ClimateCommandRequest : public ProtoMessage { +class ClimateCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 48; - static constexpr uint16_t ESTIMATED_SIZE = 83; + static constexpr uint16_t ESTIMATED_SIZE = 88; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "climate_command_request"; } #endif - uint32_t key{0}; bool has_mode{false}; enums::ClimateMode mode{}; bool has_target_temperature{false}; @@ -1487,14 +1490,13 @@ class NumberStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class NumberCommandRequest : public ProtoMessage { +class NumberCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 51; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint16_t ESTIMATED_SIZE = 14; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "number_command_request"; } #endif - uint32_t key{0}; float state{0.0f}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1504,6 +1506,7 @@ class NumberCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_SELECT @@ -1546,14 +1549,13 @@ class SelectStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SelectCommandRequest : public ProtoMessage { +class SelectCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 54; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint16_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "select_command_request"; } #endif - uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -1564,6 +1566,7 @@ class SelectCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_SIREN @@ -1606,14 +1609,13 @@ class SirenStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class SirenCommandRequest : public ProtoMessage { +class SirenCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 57; - static constexpr uint16_t ESTIMATED_SIZE = 33; + static constexpr uint16_t ESTIMATED_SIZE = 37; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "siren_command_request"; } #endif - uint32_t key{0}; bool has_state{false}; bool state{false}; bool has_tone{false}; @@ -1675,14 +1677,13 @@ class LockStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class LockCommandRequest : public ProtoMessage { +class LockCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 60; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint16_t ESTIMATED_SIZE = 22; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "lock_command_request"; } #endif - uint32_t key{0}; enums::LockCommand command{}; bool has_code{false}; std::string code{}; @@ -1718,14 +1719,13 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ButtonCommandRequest : public ProtoMessage { +class ButtonCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 62; - static constexpr uint16_t ESTIMATED_SIZE = 5; + static constexpr uint16_t ESTIMATED_SIZE = 9; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "button_command_request"; } #endif - uint32_t key{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1734,6 +1734,7 @@ class ButtonCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_MEDIA_PLAYER @@ -1794,14 +1795,13 @@ class MediaPlayerStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class MediaPlayerCommandRequest : public ProtoMessage { +class MediaPlayerCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 65; - static constexpr uint16_t ESTIMATED_SIZE = 31; + static constexpr uint16_t ESTIMATED_SIZE = 35; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "media_player_command_request"; } #endif - uint32_t key{0}; bool has_command{false}; enums::MediaPlayerCommand command{}; bool has_volume{false}; @@ -2669,14 +2669,13 @@ class AlarmControlPanelStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class AlarmControlPanelCommandRequest : public ProtoMessage { +class AlarmControlPanelCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 96; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint16_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "alarm_control_panel_command_request"; } #endif - uint32_t key{0}; enums::AlarmControlPanelStateCommand command{}; std::string code{}; void encode(ProtoWriteBuffer buffer) const override; @@ -2734,14 +2733,13 @@ class TextStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TextCommandRequest : public ProtoMessage { +class TextCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 99; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint16_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "text_command_request"; } #endif - uint32_t key{0}; std::string state{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -2752,6 +2750,7 @@ class TextCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_DATETIME_DATE @@ -2794,14 +2793,13 @@ class DateStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateCommandRequest : public ProtoMessage { +class DateCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 102; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint16_t ESTIMATED_SIZE = 21; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_command_request"; } #endif - uint32_t key{0}; uint32_t year{0}; uint32_t month{0}; uint32_t day{0}; @@ -2856,14 +2854,13 @@ class TimeStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class TimeCommandRequest : public ProtoMessage { +class TimeCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 105; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint16_t ESTIMATED_SIZE = 21; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "time_command_request"; } #endif - uint32_t key{0}; uint32_t hour{0}; uint32_t minute{0}; uint32_t second{0}; @@ -2961,14 +2958,13 @@ class ValveStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class ValveCommandRequest : public ProtoMessage { +class ValveCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 111; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint16_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "valve_command_request"; } #endif - uint32_t key{0}; bool has_position{false}; float position{0.0f}; bool stop{false}; @@ -3021,14 +3017,13 @@ class DateTimeStateResponse : public StateResponseProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class DateTimeCommandRequest : public ProtoMessage { +class DateTimeCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 114; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint16_t ESTIMATED_SIZE = 14; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_time_command_request"; } #endif - uint32_t key{0}; uint32_t epoch_seconds{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; @@ -3038,6 +3033,7 @@ class DateTimeCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_UPDATE @@ -3087,14 +3083,13 @@ class UpdateStateResponse : public StateResponseProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; -class UpdateCommandRequest : public ProtoMessage { +class UpdateCommandRequest : public CommandProtoMessage { public: static constexpr uint16_t MESSAGE_TYPE = 118; - static constexpr uint16_t ESTIMATED_SIZE = 7; + static constexpr uint16_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "update_command_request"; } #endif - uint32_t key{0}; enums::UpdateCommand command{}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(uint32_t &total_size) const override; diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index 48ddd42d61..7991e20bc5 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -986,6 +986,11 @@ void CoverCommandRequest::dump_to(std::string &out) const { out.append(" stop: "); out.append(YESNO(this->stop)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -1146,6 +1151,11 @@ void FanCommandRequest::dump_to(std::string &out) const { out.append(" preset_mode: "); out.append("'").append(this->preset_mode).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -1419,6 +1429,11 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append(" effect: "); out.append("'").append(this->effect).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -1586,6 +1601,11 @@ void SwitchCommandRequest::dump_to(std::string &out) const { out.append(" state: "); out.append(YESNO(this->state)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -1944,6 +1964,11 @@ void CameraImageResponse::dump_to(std::string &out) const { out.append(" done: "); out.append(YESNO(this->done)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } void CameraImageRequest::dump_to(std::string &out) const { @@ -2263,6 +2288,11 @@ void ClimateCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%g", this->target_humidity); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2367,6 +2397,11 @@ void NumberCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%g", this->state); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2448,6 +2483,11 @@ void SelectCommandRequest::dump_to(std::string &out) const { out.append(" state: "); out.append("'").append(this->state).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2563,6 +2603,11 @@ void SirenCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%g", this->volume); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2658,6 +2703,11 @@ void LockCommandRequest::dump_to(std::string &out) const { out.append(" code: "); out.append("'").append(this->code).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2711,6 +2761,11 @@ void ButtonCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%" PRIu32, this->key); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -2857,6 +2912,11 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { out.append(" announcement: "); out.append(YESNO(this->announcement)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -3682,6 +3742,11 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const { out.append(" code: "); out.append("'").append(this->code).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -3775,6 +3840,11 @@ void TextCommandRequest::dump_to(std::string &out) const { out.append(" state: "); out.append("'").append(this->state).append("'"); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -3872,6 +3942,11 @@ void DateCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%" PRIu32, this->day); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -3969,6 +4044,11 @@ void TimeCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%" PRIu32, this->second); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -4138,6 +4218,11 @@ void ValveCommandRequest::dump_to(std::string &out) const { out.append(" stop: "); out.append(YESNO(this->stop)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -4215,6 +4300,11 @@ void DateTimeCommandRequest::dump_to(std::string &out) const { snprintf(buffer, sizeof(buffer), "%" PRIu32, this->epoch_seconds); out.append(buffer); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif @@ -4323,6 +4413,11 @@ void UpdateCommandRequest::dump_to(std::string &out) const { out.append(" command: "); out.append(proto_enum_to_string(this->command)); out.append("\n"); + + out.append(" device_id: "); + snprintf(buffer, sizeof(buffer), "%" PRIu32, this->device_id); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif From ccd30110b1efa5da11a9559bc216f902176390e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 09:11:45 -1000 Subject: [PATCH 09/90] Fix scheduler crash when cancelling items with NULL names (#9444) --- esphome/core/scheduler.cpp | 21 +++---- esphome/core/scheduler.h | 3 - .../fixtures/scheduler_null_name.yaml | 43 ++++++++++++++ tests/integration/test_scheduler_null_name.py | 59 +++++++++++++++++++ 4 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 tests/integration/fixtures/scheduler_null_name.yaml create mode 100644 tests/integration/test_scheduler_null_name.py diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index d3da003a88..c6893b128f 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -66,10 +66,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type if (delay == SCHEDULER_DONT_RUN) { // Still need to cancel existing timer if name is not empty - if (this->is_name_valid_(name_cstr)) { - LockGuard guard{this->lock_}; - this->cancel_item_locked_(component, name_cstr, type); - } + LockGuard guard{this->lock_}; + this->cancel_item_locked_(component, name_cstr, type); return; } @@ -125,10 +123,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type LockGuard guard{this->lock_}; // If name is provided, do atomic cancel-and-add - if (this->is_name_valid_(name_cstr)) { - // Cancel existing items - this->cancel_item_locked_(component, name_cstr, type); - } + // Cancel existing items + this->cancel_item_locked_(component, name_cstr, type); // Add new item directly to to_add_ // since we have the lock held this->to_add_.push_back(std::move(item)); @@ -442,10 +438,6 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co // Get the name as const char* const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr); - // Handle null or empty names - if (!this->is_name_valid_(name_cstr)) - return false; - // obtain lock because this function iterates and can be called from non-loop task context LockGuard guard{this->lock_}; return this->cancel_item_locked_(component, name_cstr, type); @@ -453,6 +445,11 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co // Helper to cancel items by name - must be called with lock held bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type) { + // Early return if name is invalid - no items to cancel + if (name_cstr == nullptr || name_cstr[0] == '\0') { + return false; + } + size_t total_cancelled = 0; // Check all containers for matching items diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index 084ff699c5..39cee5a876 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -150,9 +150,6 @@ class Scheduler { return is_static_string ? static_cast(name_ptr) : static_cast(name_ptr)->c_str(); } - // Helper to check if a name is valid (not null and not empty) - inline bool is_name_valid_(const char *name) { return name != nullptr && name[0] != '\0'; } - // Common implementation for cancel operations bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type); diff --git a/tests/integration/fixtures/scheduler_null_name.yaml b/tests/integration/fixtures/scheduler_null_name.yaml new file mode 100644 index 0000000000..42eaacdd43 --- /dev/null +++ b/tests/integration/fixtures/scheduler_null_name.yaml @@ -0,0 +1,43 @@ +esphome: + name: scheduler-null-name + +host: + +logger: + level: DEBUG + +api: + services: + - service: test_null_name + then: + - lambda: |- + // First, create a scenario that would trigger the crash + // The crash happens when defer() is called with a name that would be cancelled + + // Test 1: Create a defer with a valid name + App.scheduler.set_timeout(nullptr, "test_defer", 0, []() { + ESP_LOGI("TEST", "First defer should be cancelled"); + }); + + // Test 2: Create another defer with the same name - this triggers cancel_item_locked_ + // In the unfixed code, this would crash if the name was NULL + App.scheduler.set_timeout(nullptr, "test_defer", 0, []() { + ESP_LOGI("TEST", "Second defer executed"); + }); + + // Test 3: Now test with nullptr - this is the actual crash scenario + // Create a defer item without a name (like voice assistant does) + const char* null_name = nullptr; + App.scheduler.set_timeout(nullptr, null_name, 0, []() { + ESP_LOGI("TEST", "Defer with null name executed"); + }); + + // Test 4: Create another defer with null name - this would trigger the crash + App.scheduler.set_timeout(nullptr, null_name, 0, []() { + ESP_LOGI("TEST", "Second null defer executed"); + }); + + // Test 5: Verify scheduler still works + App.scheduler.set_timeout(nullptr, "valid_timeout", 50, []() { + ESP_LOGI("TEST", "Test completed successfully"); + }); diff --git a/tests/integration/test_scheduler_null_name.py b/tests/integration/test_scheduler_null_name.py new file mode 100644 index 0000000000..41bcd8aed7 --- /dev/null +++ b/tests/integration/test_scheduler_null_name.py @@ -0,0 +1,59 @@ +"""Test that scheduler handles NULL names safely without crashing.""" + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_scheduler_null_name( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that scheduler handles NULL names safely without crashing.""" + + loop = asyncio.get_running_loop() + test_complete_future: asyncio.Future[bool] = loop.create_future() + + # Pattern to match test completion + test_complete_pattern = re.compile(r"Test completed successfully") + + def check_output(line: str) -> None: + """Check log output for test completion.""" + if not test_complete_future.done() and test_complete_pattern.search(line): + test_complete_future.set_result(True) + + async with run_compiled(yaml_config, line_callback=check_output): + async with api_client_connected() as client: + # Verify we can connect + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "scheduler-null-name" + + # List services + _, services = await asyncio.wait_for( + client.list_entities_services(), timeout=5.0 + ) + + # Find our test service + test_null_name_service = next( + (s for s in services if s.name == "test_null_name"), None + ) + assert test_null_name_service is not None, ( + "test_null_name service not found" + ) + + # Execute the test + client.execute_service(test_null_name_service, {}) + + # Wait for test completion + try: + await asyncio.wait_for(test_complete_future, timeout=10.0) + except asyncio.TimeoutError: + pytest.fail( + "Test did not complete within timeout - likely crashed due to NULL name" + ) From bab3deee1bfa7319f0682c2184821d7b18e987f5 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Fri, 11 Jul 2025 16:35:52 -0700 Subject: [PATCH 10/90] [wizard] use lowercase to match (#9448) Co-authored-by: Samuel Sieb --- esphome/wizard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 1826487aa4..6f7cbd1ff4 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -411,7 +411,7 @@ def wizard(path): safe_print("Options:") for board_id, board_data in boards_list: safe_print(f" - {board_id} - {board_data['name']}") - boards.append(board_id) + boards.append(board_id.lower()) while True: board = safe_input(color(AnsiFore.BOLD_WHITE, "(board): ")) From 1b222ceca3c19ac522d0a6856350ceab81c563d4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 14:38:23 -1000 Subject: [PATCH 11/90] Optimize API flash usage by storing message size at compile time (#9447) --- esphome/components/api/api_connection.cpp | 226 ++------- esphome/components/api/api_connection.h | 51 +- esphome/components/api/api_frame_helper.cpp | 4 +- esphome/components/api/api_frame_helper.h | 16 +- esphome/components/api/api_pb2.h | 508 ++++++++++---------- esphome/components/api/api_server.cpp | 3 +- esphome/components/api/list_entities.h | 2 +- esphome/components/api/proto.h | 4 +- script/api_protobuf/api_protobuf.py | 15 +- 9 files changed, 358 insertions(+), 471 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 537d75467f..3b0b4858a9 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -193,14 +193,15 @@ void APIConnection::loop() { // If we can't send the ping request directly (tx_buffer full), // schedule it at the front of the batch so it will be sent with priority ESP_LOGW(TAG, "Buffer full, ping queued"); - this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE); + this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE, + PingRequest::ESTIMATED_SIZE); this->flags_.sent_ping = true; // Mark as sent to avoid scheduling multiple pings } } #ifdef USE_CAMERA if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) { - uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_->available()); + uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available()); bool done = this->image_reader_->available() == to_send; uint32_t msg_size = 0; ProtoSize::add_fixed_field<4>(msg_size, 1, true); @@ -265,7 +266,7 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) { // Encodes a message to the buffer and returns the total number of bytes used, // including header and footer overhead. Returns 0 if the message doesn't fit. -uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, +uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { #ifdef HAS_PROTO_MESSAGE_DUMP // If in log-only mode, just log and return @@ -316,7 +317,7 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t mes #ifdef USE_BINARY_SENSOR bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) { return this->send_message_smart_(binary_sensor, &APIConnection::try_send_binary_sensor_state, - BinarySensorStateResponse::MESSAGE_TYPE); + BinarySensorStateResponse::MESSAGE_TYPE, BinarySensorStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -343,7 +344,8 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne #ifdef USE_COVER bool APIConnection::send_cover_state(cover::Cover *cover) { - return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE, + CoverStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -400,7 +402,8 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) { #ifdef USE_FAN bool APIConnection::send_fan_state(fan::Fan *fan) { - return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE, + FanStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -455,7 +458,8 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { #ifdef USE_LIGHT bool APIConnection::send_light_state(light::LightState *light) { - return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE, + LightStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -543,7 +547,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) { #ifdef USE_SENSOR bool APIConnection::send_sensor_state(sensor::Sensor *sensor) { - return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE, + SensorStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -575,7 +580,8 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection * #ifdef USE_SWITCH bool APIConnection::send_switch_state(switch_::Switch *a_switch) { - return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE, + SwitchStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -611,7 +617,7 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) { #ifdef USE_TEXT_SENSOR bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) { return this->send_message_smart_(text_sensor, &APIConnection::try_send_text_sensor_state, - TextSensorStateResponse::MESSAGE_TYPE); + TextSensorStateResponse::MESSAGE_TYPE, TextSensorStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -638,7 +644,8 @@ uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnect #ifdef USE_CLIMATE bool APIConnection::send_climate_state(climate::Climate *climate) { - return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE, + ClimateStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -734,7 +741,8 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { #ifdef USE_NUMBER bool APIConnection::send_number_state(number::Number *number) { - return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE, + NumberStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -770,7 +778,8 @@ void APIConnection::number_command(const NumberCommandRequest &msg) { #ifdef USE_DATETIME_DATE bool APIConnection::send_date_state(datetime::DateEntity *date) { - return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE, + DateStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -800,7 +809,8 @@ void APIConnection::date_command(const DateCommandRequest &msg) { #ifdef USE_DATETIME_TIME bool APIConnection::send_time_state(datetime::TimeEntity *time) { - return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE, + TimeStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -831,7 +841,7 @@ void APIConnection::time_command(const TimeCommandRequest &msg) { #ifdef USE_DATETIME_DATETIME bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) { return this->send_message_smart_(datetime, &APIConnection::try_send_datetime_state, - DateTimeStateResponse::MESSAGE_TYPE); + DateTimeStateResponse::MESSAGE_TYPE, DateTimeStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -862,7 +872,8 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) { #ifdef USE_TEXT bool APIConnection::send_text_state(text::Text *text) { - return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE, + TextStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -896,7 +907,8 @@ void APIConnection::text_command(const TextCommandRequest &msg) { #ifdef USE_SELECT bool APIConnection::send_select_state(select::Select *select) { - return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE, + SelectStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -944,7 +956,8 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg #ifdef USE_LOCK bool APIConnection::send_lock_state(lock::Lock *a_lock) { - return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE, + LockStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, @@ -986,7 +999,8 @@ void APIConnection::lock_command(const LockCommandRequest &msg) { #ifdef USE_VALVE bool APIConnection::send_valve_state(valve::Valve *valve) { - return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE, + ValveStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -1023,7 +1037,7 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) { #ifdef USE_MEDIA_PLAYER bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) { return this->send_message_smart_(media_player, &APIConnection::try_send_media_player_state, - MediaPlayerStateResponse::MESSAGE_TYPE); + MediaPlayerStateResponse::MESSAGE_TYPE, MediaPlayerStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -1262,7 +1276,8 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon #ifdef USE_ALARM_CONTROL_PANEL bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { return this->send_message_smart_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state, - AlarmControlPanelStateResponse::MESSAGE_TYPE); + AlarmControlPanelStateResponse::MESSAGE_TYPE, + AlarmControlPanelStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -1316,7 +1331,8 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe #ifdef USE_EVENT void APIConnection::send_event(event::Event *event, const std::string &event_type) { - this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE); + this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE, + EventResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -1341,7 +1357,8 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c #ifdef USE_UPDATE bool APIConnection::send_update_state(update::UpdateEntity *update) { - return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE); + return this->send_message_smart_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE, + UpdateStateResponse::ESTIMATED_SIZE); } uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) { @@ -1588,7 +1605,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) { } return false; } -bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) { +bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) { if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse return false; } @@ -1622,7 +1639,8 @@ void APIConnection::on_fatal_error() { this->flags_.remove = true; } -void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) { +void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type, + uint8_t estimated_size) { // Check if we already have a message of this type for this entity // This provides deduplication per entity/message_type combination // O(n) but optimized for RAM and not performance. @@ -1637,12 +1655,13 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c } // No existing item found, add new one - items.emplace_back(entity, std::move(creator), message_type); + items.emplace_back(entity, std::move(creator), message_type, estimated_size); } -void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type) { +void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type, + uint8_t estimated_size) { // Insert at front for high priority messages (no deduplication check) - items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type)); + items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type, estimated_size)); } bool APIConnection::schedule_batch_() { @@ -1714,7 +1733,7 @@ void APIConnection::process_batch_() { uint32_t total_estimated_size = 0; for (size_t i = 0; i < this->deferred_batch_.size(); i++) { const auto &item = this->deferred_batch_[i]; - total_estimated_size += get_estimated_message_size(item.message_type); + total_estimated_size += item.estimated_size; } // Calculate total overhead for all messages @@ -1752,9 +1771,9 @@ void APIConnection::process_batch_() { // Update tracking variables items_processed++; - // After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation + // After first message, set remaining size to MAX_BATCH_PACKET_SIZE to avoid fragmentation if (items_processed == 1) { - remaining_size = MAX_PACKET_SIZE; + remaining_size = MAX_BATCH_PACKET_SIZE; } remaining_size -= payload_size; // Calculate where the next message's header padding will start @@ -1808,7 +1827,7 @@ void APIConnection::process_batch_() { } uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, - bool is_single, uint16_t message_type) const { + bool is_single, uint8_t message_type) const { #ifdef USE_EVENT // Special case: EventResponse uses string pointer if (message_type == EventResponse::MESSAGE_TYPE) { @@ -1839,149 +1858,6 @@ uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single); } -uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) { - // Use generated ESTIMATED_SIZE constants from each message type - switch (message_type) { -#ifdef USE_BINARY_SENSOR - case BinarySensorStateResponse::MESSAGE_TYPE: - return BinarySensorStateResponse::ESTIMATED_SIZE; - case ListEntitiesBinarySensorResponse::MESSAGE_TYPE: - return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_SENSOR - case SensorStateResponse::MESSAGE_TYPE: - return SensorStateResponse::ESTIMATED_SIZE; - case ListEntitiesSensorResponse::MESSAGE_TYPE: - return ListEntitiesSensorResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_SWITCH - case SwitchStateResponse::MESSAGE_TYPE: - return SwitchStateResponse::ESTIMATED_SIZE; - case ListEntitiesSwitchResponse::MESSAGE_TYPE: - return ListEntitiesSwitchResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_TEXT_SENSOR - case TextSensorStateResponse::MESSAGE_TYPE: - return TextSensorStateResponse::ESTIMATED_SIZE; - case ListEntitiesTextSensorResponse::MESSAGE_TYPE: - return ListEntitiesTextSensorResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_NUMBER - case NumberStateResponse::MESSAGE_TYPE: - return NumberStateResponse::ESTIMATED_SIZE; - case ListEntitiesNumberResponse::MESSAGE_TYPE: - return ListEntitiesNumberResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_TEXT - case TextStateResponse::MESSAGE_TYPE: - return TextStateResponse::ESTIMATED_SIZE; - case ListEntitiesTextResponse::MESSAGE_TYPE: - return ListEntitiesTextResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_SELECT - case SelectStateResponse::MESSAGE_TYPE: - return SelectStateResponse::ESTIMATED_SIZE; - case ListEntitiesSelectResponse::MESSAGE_TYPE: - return ListEntitiesSelectResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_LOCK - case LockStateResponse::MESSAGE_TYPE: - return LockStateResponse::ESTIMATED_SIZE; - case ListEntitiesLockResponse::MESSAGE_TYPE: - return ListEntitiesLockResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_EVENT - case EventResponse::MESSAGE_TYPE: - return EventResponse::ESTIMATED_SIZE; - case ListEntitiesEventResponse::MESSAGE_TYPE: - return ListEntitiesEventResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_COVER - case CoverStateResponse::MESSAGE_TYPE: - return CoverStateResponse::ESTIMATED_SIZE; - case ListEntitiesCoverResponse::MESSAGE_TYPE: - return ListEntitiesCoverResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_FAN - case FanStateResponse::MESSAGE_TYPE: - return FanStateResponse::ESTIMATED_SIZE; - case ListEntitiesFanResponse::MESSAGE_TYPE: - return ListEntitiesFanResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_LIGHT - case LightStateResponse::MESSAGE_TYPE: - return LightStateResponse::ESTIMATED_SIZE; - case ListEntitiesLightResponse::MESSAGE_TYPE: - return ListEntitiesLightResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_CLIMATE - case ClimateStateResponse::MESSAGE_TYPE: - return ClimateStateResponse::ESTIMATED_SIZE; - case ListEntitiesClimateResponse::MESSAGE_TYPE: - return ListEntitiesClimateResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_CAMERA - case ListEntitiesCameraResponse::MESSAGE_TYPE: - return ListEntitiesCameraResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_BUTTON - case ListEntitiesButtonResponse::MESSAGE_TYPE: - return ListEntitiesButtonResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_MEDIA_PLAYER - case MediaPlayerStateResponse::MESSAGE_TYPE: - return MediaPlayerStateResponse::ESTIMATED_SIZE; - case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE: - return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_ALARM_CONTROL_PANEL - case AlarmControlPanelStateResponse::MESSAGE_TYPE: - return AlarmControlPanelStateResponse::ESTIMATED_SIZE; - case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE: - return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_DATETIME_DATE - case DateStateResponse::MESSAGE_TYPE: - return DateStateResponse::ESTIMATED_SIZE; - case ListEntitiesDateResponse::MESSAGE_TYPE: - return ListEntitiesDateResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_DATETIME_TIME - case TimeStateResponse::MESSAGE_TYPE: - return TimeStateResponse::ESTIMATED_SIZE; - case ListEntitiesTimeResponse::MESSAGE_TYPE: - return ListEntitiesTimeResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_DATETIME_DATETIME - case DateTimeStateResponse::MESSAGE_TYPE: - return DateTimeStateResponse::ESTIMATED_SIZE; - case ListEntitiesDateTimeResponse::MESSAGE_TYPE: - return ListEntitiesDateTimeResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_VALVE - case ValveStateResponse::MESSAGE_TYPE: - return ValveStateResponse::ESTIMATED_SIZE; - case ListEntitiesValveResponse::MESSAGE_TYPE: - return ListEntitiesValveResponse::ESTIMATED_SIZE; -#endif -#ifdef USE_UPDATE - case UpdateStateResponse::MESSAGE_TYPE: - return UpdateStateResponse::ESTIMATED_SIZE; - case ListEntitiesUpdateResponse::MESSAGE_TYPE: - return ListEntitiesUpdateResponse::ESTIMATED_SIZE; -#endif - case ListEntitiesServicesResponse::MESSAGE_TYPE: - return ListEntitiesServicesResponse::ESTIMATED_SIZE; - case ListEntitiesDoneResponse::MESSAGE_TYPE: - return ListEntitiesDoneResponse::ESTIMATED_SIZE; - case DisconnectRequest::MESSAGE_TYPE: - return DisconnectRequest::ESTIMATED_SIZE; - default: - // Fallback for unknown message types - return 24; - } -} - } // namespace api } // namespace esphome #endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index b70b037999..fdc2fb3529 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -33,7 +33,7 @@ class APIConnection : public APIServerConnection { bool send_list_info_done() { return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done, - ListEntitiesDoneResponse::MESSAGE_TYPE); + ListEntitiesDoneResponse::MESSAGE_TYPE, ListEntitiesDoneResponse::ESTIMATED_SIZE); } #ifdef USE_BINARY_SENSOR bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor); @@ -256,7 +256,7 @@ class APIConnection : public APIServerConnection { } bool try_to_clear_buffer(bool log_out_of_space); - bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override; + bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override; std::string get_client_combined_info() const { if (this->client_info_ == this->client_peername_) { @@ -298,7 +298,7 @@ class APIConnection : public APIServerConnection { } // Non-template helper to encode any ProtoMessage - static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn, + static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn, uint32_t remaining_size, bool is_single); #ifdef USE_VOICE_ASSISTANT @@ -443,9 +443,6 @@ class APIConnection : public APIServerConnection { static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); - // Helper function to get estimated message size for buffer pre-allocation - static uint16_t get_estimated_message_size(uint16_t message_type); - // Batch message method for ping requests static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single); @@ -505,10 +502,10 @@ class APIConnection : public APIServerConnection { // Call operator - uses message_type to determine union type uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, - uint16_t message_type) const; + uint8_t message_type) const; // Manual cleanup method - must be called before destruction for string types - void cleanup(uint16_t message_type) { + void cleanup(uint8_t message_type) { #ifdef USE_EVENT if (message_type == EventResponse::MESSAGE_TYPE && data_.string_ptr != nullptr) { delete data_.string_ptr; @@ -529,11 +526,12 @@ class APIConnection : public APIServerConnection { struct BatchItem { EntityBase *entity; // Entity pointer MessageCreator creator; // Function that creates the message when needed - uint16_t message_type; // Message type for overhead calculation + uint8_t message_type; // Message type for overhead calculation (max 255) + uint8_t estimated_size; // Estimated message size (max 255 bytes) // Constructor for creating BatchItem - BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type) - : entity(entity), creator(std::move(creator)), message_type(message_type) {} + BatchItem(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) + : entity(entity), creator(std::move(creator)), message_type(message_type), estimated_size(estimated_size) {} }; std::vector items; @@ -559,9 +557,9 @@ class APIConnection : public APIServerConnection { } // Add item to the batch - void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type); + void add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size); // Add item to the front of the batch (for high priority messages like ping) - void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type); + void add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size); // Clear all items with proper cleanup void clear() { @@ -630,7 +628,7 @@ class APIConnection : public APIServerConnection { // to send in one go. This is the maximum size of a single packet // that can be sent over the network. // This is to avoid fragmentation of the packet. - static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU + static constexpr size_t MAX_BATCH_PACKET_SIZE = 1390; // MTU bool schedule_batch_(); void process_batch_(); @@ -641,9 +639,9 @@ class APIConnection : public APIServerConnection { #ifdef HAS_PROTO_MESSAGE_DUMP // Helper to log a proto message from a MessageCreator object - void log_proto_message_(EntityBase *entity, const MessageCreator &creator, uint16_t message_type) { + void log_proto_message_(EntityBase *entity, const MessageCreator &creator, uint8_t message_type) { this->flags_.log_only_mode = true; - creator(entity, this, MAX_PACKET_SIZE, true, message_type); + creator(entity, this, MAX_BATCH_PACKET_SIZE, true, message_type); this->flags_.log_only_mode = false; } @@ -654,7 +652,8 @@ class APIConnection : public APIServerConnection { #endif // Helper method to send a message either immediately or via batching - bool send_message_smart_(EntityBase *entity, MessageCreatorPtr creator, uint16_t message_type) { + bool send_message_smart_(EntityBase *entity, MessageCreatorPtr creator, uint8_t message_type, + uint8_t estimated_size) { // Try to send immediately if: // 1. We should try to send immediately (should_try_send_immediately = true) // 2. Batch delay is 0 (user has opted in to immediate sending) @@ -662,7 +661,7 @@ class APIConnection : public APIServerConnection { if (this->flags_.should_try_send_immediately && this->get_batch_delay_ms_() == 0 && this->helper_->can_write_without_blocking()) { // Now actually encode and send - if (creator(entity, this, MAX_PACKET_SIZE, true) && + if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true) && this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) { #ifdef HAS_PROTO_MESSAGE_DUMP // Log the message in verbose mode @@ -675,23 +674,25 @@ class APIConnection : public APIServerConnection { } // Fall back to scheduled batching - return this->schedule_message_(entity, creator, message_type); + return this->schedule_message_(entity, creator, message_type, estimated_size); } // Helper function to schedule a deferred message with known message type - bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) { - this->deferred_batch_.add_item(entity, std::move(creator), message_type); + bool schedule_message_(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size) { + this->deferred_batch_.add_item(entity, std::move(creator), message_type, estimated_size); return this->schedule_batch_(); } // Overload for function pointers (for info messages and current state reads) - bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) { - return schedule_message_(entity, MessageCreator(function_ptr), message_type); + bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint8_t message_type, + uint8_t estimated_size) { + return schedule_message_(entity, MessageCreator(function_ptr), message_type, estimated_size); } // Helper function to schedule a high priority message at the front of the batch - bool schedule_message_front_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) { - this->deferred_batch_.add_item_front(entity, MessageCreator(function_ptr), message_type); + bool schedule_message_front_(EntityBase *entity, MessageCreatorPtr function_ptr, uint8_t message_type, + uint8_t estimated_size) { + this->deferred_batch_.add_item_front(entity, MessageCreator(function_ptr), message_type, estimated_size); return this->schedule_batch_(); } }; diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 2f5acc3bfa..156fd42cb3 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -613,7 +613,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) { buffer->type = type; return APIError::OK; } -APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { +APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) { // Resize to include MAC space (required for Noise encryption) buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_); PacketInfo packet{type, 0, @@ -1002,7 +1002,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { buffer->type = rx_header_parsed_type_; return APIError::OK; } -APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) { +APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) { PacketInfo packet{type, 0, static_cast(buffer.get_buffer()->size() - frame_header_padding_)}; return write_protobuf_packets(buffer, std::span(&packet, 1)); } diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index eae83a3484..4bcc4acd61 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -30,13 +30,11 @@ struct ReadPacketBuffer { // Packed packet info structure to minimize memory usage struct PacketInfo { - uint16_t message_type; // 2 bytes - uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes) - uint16_t payload_size; // 2 bytes (up to 65535 bytes) - uint16_t padding; // 2 byte (for alignment) + uint16_t offset; // Offset in buffer where message starts + uint16_t payload_size; // Size of the message payload + uint8_t message_type; // Message type (0-255) - PacketInfo(uint16_t type, uint16_t off, uint16_t size) - : message_type(type), offset(off), payload_size(size), padding(0) {} + PacketInfo(uint8_t type, uint16_t off, uint16_t size) : offset(off), payload_size(size), message_type(type) {} }; enum class APIError : uint16_t { @@ -98,7 +96,7 @@ class APIFrameHelper { } // Give this helper a name for logging void set_log_info(std::string info) { info_ = std::move(info); } - virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0; + virtual APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) = 0; // Write multiple protobuf packets in a single operation // packets contains (message_type, offset, length) for each message in the buffer // The buffer contains all messages with appropriate padding before each @@ -197,7 +195,7 @@ class APINoiseFrameHelper : public APIFrameHelper { APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; - APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; + APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override; APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span packets) override; // Get the frame header padding required by this protocol uint8_t frame_header_padding() override { return frame_header_padding_; } @@ -251,7 +249,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper { APIError init() override; APIError loop() override; APIError read_packet(ReadPacketBuffer *buffer) override; - APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override; + APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override; APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span packets) override; uint8_t frame_header_padding() override { return frame_header_padding_; } // Get the frame footer size required by this protocol diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 029f22dfc2..3c4e0dfb6d 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -318,8 +318,8 @@ class CommandProtoMessage : public ProtoMessage { }; class HelloRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 1; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint8_t MESSAGE_TYPE = 1; + static constexpr uint8_t ESTIMATED_SIZE = 17; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "hello_request"; } #endif @@ -338,8 +338,8 @@ class HelloRequest : public ProtoMessage { }; class HelloResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 2; - static constexpr uint16_t ESTIMATED_SIZE = 26; + static constexpr uint8_t MESSAGE_TYPE = 2; + static constexpr uint8_t ESTIMATED_SIZE = 26; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "hello_response"; } #endif @@ -359,8 +359,8 @@ class HelloResponse : public ProtoMessage { }; class ConnectRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 3; - static constexpr uint16_t ESTIMATED_SIZE = 9; + static constexpr uint8_t MESSAGE_TYPE = 3; + static constexpr uint8_t ESTIMATED_SIZE = 9; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "connect_request"; } #endif @@ -376,8 +376,8 @@ class ConnectRequest : public ProtoMessage { }; class ConnectResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 4; - static constexpr uint16_t ESTIMATED_SIZE = 2; + static constexpr uint8_t MESSAGE_TYPE = 4; + static constexpr uint8_t ESTIMATED_SIZE = 2; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "connect_response"; } #endif @@ -393,8 +393,8 @@ class ConnectResponse : public ProtoMessage { }; class DisconnectRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 5; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 5; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "disconnect_request"; } #endif @@ -406,8 +406,8 @@ class DisconnectRequest : public ProtoMessage { }; class DisconnectResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 6; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 6; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "disconnect_response"; } #endif @@ -419,8 +419,8 @@ class DisconnectResponse : public ProtoMessage { }; class PingRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 7; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 7; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "ping_request"; } #endif @@ -432,8 +432,8 @@ class PingRequest : public ProtoMessage { }; class PingResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 8; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 8; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "ping_response"; } #endif @@ -445,8 +445,8 @@ class PingResponse : public ProtoMessage { }; class DeviceInfoRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 9; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 9; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "device_info_request"; } #endif @@ -487,8 +487,8 @@ class DeviceInfo : public ProtoMessage { }; class DeviceInfoResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 10; - static constexpr uint16_t ESTIMATED_SIZE = 219; + static constexpr uint8_t MESSAGE_TYPE = 10; + static constexpr uint8_t ESTIMATED_SIZE = 219; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "device_info_response"; } #endif @@ -526,8 +526,8 @@ class DeviceInfoResponse : public ProtoMessage { }; class ListEntitiesRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 11; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 11; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_request"; } #endif @@ -539,8 +539,8 @@ class ListEntitiesRequest : public ProtoMessage { }; class ListEntitiesDoneResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 19; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 19; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_done_response"; } #endif @@ -552,8 +552,8 @@ class ListEntitiesDoneResponse : public ProtoMessage { }; class SubscribeStatesRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 20; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 20; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_states_request"; } #endif @@ -566,8 +566,8 @@ class SubscribeStatesRequest : public ProtoMessage { #ifdef USE_BINARY_SENSOR class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 12; - static constexpr uint16_t ESTIMATED_SIZE = 60; + static constexpr uint8_t MESSAGE_TYPE = 12; + static constexpr uint8_t ESTIMATED_SIZE = 60; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_binary_sensor_response"; } #endif @@ -586,8 +586,8 @@ class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { }; class BinarySensorStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 21; - static constexpr uint16_t ESTIMATED_SIZE = 13; + static constexpr uint8_t MESSAGE_TYPE = 21; + static constexpr uint8_t ESTIMATED_SIZE = 13; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "binary_sensor_state_response"; } #endif @@ -607,8 +607,8 @@ class BinarySensorStateResponse : public StateResponseProtoMessage { #ifdef USE_COVER class ListEntitiesCoverResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 13; - static constexpr uint16_t ESTIMATED_SIZE = 66; + static constexpr uint8_t MESSAGE_TYPE = 13; + static constexpr uint8_t ESTIMATED_SIZE = 66; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_cover_response"; } #endif @@ -630,8 +630,8 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage { }; class CoverStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 22; - static constexpr uint16_t ESTIMATED_SIZE = 23; + static constexpr uint8_t MESSAGE_TYPE = 22; + static constexpr uint8_t ESTIMATED_SIZE = 23; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "cover_state_response"; } #endif @@ -651,8 +651,8 @@ class CoverStateResponse : public StateResponseProtoMessage { }; class CoverCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 30; - static constexpr uint16_t ESTIMATED_SIZE = 29; + static constexpr uint8_t MESSAGE_TYPE = 30; + static constexpr uint8_t ESTIMATED_SIZE = 29; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "cover_command_request"; } #endif @@ -677,8 +677,8 @@ class CoverCommandRequest : public CommandProtoMessage { #ifdef USE_FAN class ListEntitiesFanResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 14; - static constexpr uint16_t ESTIMATED_SIZE = 77; + static constexpr uint8_t MESSAGE_TYPE = 14; + static constexpr uint8_t ESTIMATED_SIZE = 77; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_fan_response"; } #endif @@ -700,8 +700,8 @@ class ListEntitiesFanResponse : public InfoResponseProtoMessage { }; class FanStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 23; - static constexpr uint16_t ESTIMATED_SIZE = 30; + static constexpr uint8_t MESSAGE_TYPE = 23; + static constexpr uint8_t ESTIMATED_SIZE = 30; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "fan_state_response"; } #endif @@ -724,8 +724,8 @@ class FanStateResponse : public StateResponseProtoMessage { }; class FanCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 31; - static constexpr uint16_t ESTIMATED_SIZE = 42; + static constexpr uint8_t MESSAGE_TYPE = 31; + static constexpr uint8_t ESTIMATED_SIZE = 42; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "fan_command_request"; } #endif @@ -756,8 +756,8 @@ class FanCommandRequest : public CommandProtoMessage { #ifdef USE_LIGHT class ListEntitiesLightResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 15; - static constexpr uint16_t ESTIMATED_SIZE = 90; + static constexpr uint8_t MESSAGE_TYPE = 15; + static constexpr uint8_t ESTIMATED_SIZE = 90; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_light_response"; } #endif @@ -782,8 +782,8 @@ class ListEntitiesLightResponse : public InfoResponseProtoMessage { }; class LightStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 24; - static constexpr uint16_t ESTIMATED_SIZE = 67; + static constexpr uint8_t MESSAGE_TYPE = 24; + static constexpr uint8_t ESTIMATED_SIZE = 67; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "light_state_response"; } #endif @@ -812,8 +812,8 @@ class LightStateResponse : public StateResponseProtoMessage { }; class LightCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 32; - static constexpr uint16_t ESTIMATED_SIZE = 112; + static constexpr uint8_t MESSAGE_TYPE = 32; + static constexpr uint8_t ESTIMATED_SIZE = 112; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "light_command_request"; } #endif @@ -858,8 +858,8 @@ class LightCommandRequest : public CommandProtoMessage { #ifdef USE_SENSOR class ListEntitiesSensorResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 16; - static constexpr uint16_t ESTIMATED_SIZE = 77; + static constexpr uint8_t MESSAGE_TYPE = 16; + static constexpr uint8_t ESTIMATED_SIZE = 77; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_sensor_response"; } #endif @@ -882,8 +882,8 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage { }; class SensorStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 25; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint8_t MESSAGE_TYPE = 25; + static constexpr uint8_t ESTIMATED_SIZE = 16; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "sensor_state_response"; } #endif @@ -903,8 +903,8 @@ class SensorStateResponse : public StateResponseProtoMessage { #ifdef USE_SWITCH class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 17; - static constexpr uint16_t ESTIMATED_SIZE = 60; + static constexpr uint8_t MESSAGE_TYPE = 17; + static constexpr uint8_t ESTIMATED_SIZE = 60; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_switch_response"; } #endif @@ -923,8 +923,8 @@ class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { }; class SwitchStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 26; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 26; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "switch_state_response"; } #endif @@ -941,8 +941,8 @@ class SwitchStateResponse : public StateResponseProtoMessage { }; class SwitchCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 33; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 33; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "switch_command_request"; } #endif @@ -961,8 +961,8 @@ class SwitchCommandRequest : public CommandProtoMessage { #ifdef USE_TEXT_SENSOR class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 18; - static constexpr uint16_t ESTIMATED_SIZE = 58; + static constexpr uint8_t MESSAGE_TYPE = 18; + static constexpr uint8_t ESTIMATED_SIZE = 58; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_text_sensor_response"; } #endif @@ -980,8 +980,8 @@ class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { }; class TextSensorStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 27; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 27; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "text_sensor_state_response"; } #endif @@ -1001,8 +1001,8 @@ class TextSensorStateResponse : public StateResponseProtoMessage { #endif class SubscribeLogsRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 28; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 28; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_logs_request"; } #endif @@ -1019,8 +1019,8 @@ class SubscribeLogsRequest : public ProtoMessage { }; class SubscribeLogsResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 29; - static constexpr uint16_t ESTIMATED_SIZE = 13; + static constexpr uint8_t MESSAGE_TYPE = 29; + static constexpr uint8_t ESTIMATED_SIZE = 13; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_logs_response"; } #endif @@ -1040,8 +1040,8 @@ class SubscribeLogsResponse : public ProtoMessage { #ifdef USE_API_NOISE class NoiseEncryptionSetKeyRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 124; - static constexpr uint16_t ESTIMATED_SIZE = 9; + static constexpr uint8_t MESSAGE_TYPE = 124; + static constexpr uint8_t ESTIMATED_SIZE = 9; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "noise_encryption_set_key_request"; } #endif @@ -1057,8 +1057,8 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage { }; class NoiseEncryptionSetKeyResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 125; - static constexpr uint16_t ESTIMATED_SIZE = 2; + static constexpr uint8_t MESSAGE_TYPE = 125; + static constexpr uint8_t ESTIMATED_SIZE = 2; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "noise_encryption_set_key_response"; } #endif @@ -1075,8 +1075,8 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage { #endif class SubscribeHomeassistantServicesRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 34; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 34; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_homeassistant_services_request"; } #endif @@ -1101,8 +1101,8 @@ class HomeassistantServiceMap : public ProtoMessage { }; class HomeassistantServiceResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 35; - static constexpr uint16_t ESTIMATED_SIZE = 113; + static constexpr uint8_t MESSAGE_TYPE = 35; + static constexpr uint8_t ESTIMATED_SIZE = 113; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "homeassistant_service_response"; } #endif @@ -1123,8 +1123,8 @@ class HomeassistantServiceResponse : public ProtoMessage { }; class SubscribeHomeAssistantStatesRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 38; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 38; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_home_assistant_states_request"; } #endif @@ -1136,8 +1136,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage { }; class SubscribeHomeAssistantStateResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 39; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 39; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_home_assistant_state_response"; } #endif @@ -1156,8 +1156,8 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { }; class HomeAssistantStateResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 40; - static constexpr uint16_t ESTIMATED_SIZE = 27; + static constexpr uint8_t MESSAGE_TYPE = 40; + static constexpr uint8_t ESTIMATED_SIZE = 27; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "home_assistant_state_response"; } #endif @@ -1175,8 +1175,8 @@ class HomeAssistantStateResponse : public ProtoMessage { }; class GetTimeRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 36; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 36; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "get_time_request"; } #endif @@ -1188,8 +1188,8 @@ class GetTimeRequest : public ProtoMessage { }; class GetTimeResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 37; - static constexpr uint16_t ESTIMATED_SIZE = 5; + static constexpr uint8_t MESSAGE_TYPE = 37; + static constexpr uint8_t ESTIMATED_SIZE = 5; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "get_time_response"; } #endif @@ -1219,8 +1219,8 @@ class ListEntitiesServicesArgument : public ProtoMessage { }; class ListEntitiesServicesResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 41; - static constexpr uint16_t ESTIMATED_SIZE = 48; + static constexpr uint8_t MESSAGE_TYPE = 41; + static constexpr uint8_t ESTIMATED_SIZE = 48; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_services_response"; } #endif @@ -1261,8 +1261,8 @@ class ExecuteServiceArgument : public ProtoMessage { }; class ExecuteServiceRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 42; - static constexpr uint16_t ESTIMATED_SIZE = 39; + static constexpr uint8_t MESSAGE_TYPE = 42; + static constexpr uint8_t ESTIMATED_SIZE = 39; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "execute_service_request"; } #endif @@ -1281,8 +1281,8 @@ class ExecuteServiceRequest : public ProtoMessage { #ifdef USE_CAMERA class ListEntitiesCameraResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 43; - static constexpr uint16_t ESTIMATED_SIZE = 49; + static constexpr uint8_t MESSAGE_TYPE = 43; + static constexpr uint8_t ESTIMATED_SIZE = 49; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_camera_response"; } #endif @@ -1299,8 +1299,8 @@ class ListEntitiesCameraResponse : public InfoResponseProtoMessage { }; class CameraImageResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 44; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 44; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "camera_image_response"; } #endif @@ -1319,8 +1319,8 @@ class CameraImageResponse : public StateResponseProtoMessage { }; class CameraImageRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 45; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 45; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "camera_image_request"; } #endif @@ -1339,8 +1339,8 @@ class CameraImageRequest : public ProtoMessage { #ifdef USE_CLIMATE class ListEntitiesClimateResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 46; - static constexpr uint16_t ESTIMATED_SIZE = 156; + static constexpr uint8_t MESSAGE_TYPE = 46; + static constexpr uint8_t ESTIMATED_SIZE = 156; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_climate_response"; } #endif @@ -1375,8 +1375,8 @@ class ListEntitiesClimateResponse : public InfoResponseProtoMessage { }; class ClimateStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 47; - static constexpr uint16_t ESTIMATED_SIZE = 70; + static constexpr uint8_t MESSAGE_TYPE = 47; + static constexpr uint8_t ESTIMATED_SIZE = 70; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "climate_state_response"; } #endif @@ -1407,8 +1407,8 @@ class ClimateStateResponse : public StateResponseProtoMessage { }; class ClimateCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 48; - static constexpr uint16_t ESTIMATED_SIZE = 88; + static constexpr uint8_t MESSAGE_TYPE = 48; + static constexpr uint8_t ESTIMATED_SIZE = 88; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "climate_command_request"; } #endif @@ -1449,8 +1449,8 @@ class ClimateCommandRequest : public CommandProtoMessage { #ifdef USE_NUMBER class ListEntitiesNumberResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 49; - static constexpr uint16_t ESTIMATED_SIZE = 84; + static constexpr uint8_t MESSAGE_TYPE = 49; + static constexpr uint8_t ESTIMATED_SIZE = 84; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_number_response"; } #endif @@ -1473,8 +1473,8 @@ class ListEntitiesNumberResponse : public InfoResponseProtoMessage { }; class NumberStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 50; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint8_t MESSAGE_TYPE = 50; + static constexpr uint8_t ESTIMATED_SIZE = 16; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "number_state_response"; } #endif @@ -1492,8 +1492,8 @@ class NumberStateResponse : public StateResponseProtoMessage { }; class NumberCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 51; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint8_t MESSAGE_TYPE = 51; + static constexpr uint8_t ESTIMATED_SIZE = 14; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "number_command_request"; } #endif @@ -1512,8 +1512,8 @@ class NumberCommandRequest : public CommandProtoMessage { #ifdef USE_SELECT class ListEntitiesSelectResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 52; - static constexpr uint16_t ESTIMATED_SIZE = 67; + static constexpr uint8_t MESSAGE_TYPE = 52; + static constexpr uint8_t ESTIMATED_SIZE = 67; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_select_response"; } #endif @@ -1531,8 +1531,8 @@ class ListEntitiesSelectResponse : public InfoResponseProtoMessage { }; class SelectStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 53; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 53; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "select_state_response"; } #endif @@ -1551,8 +1551,8 @@ class SelectStateResponse : public StateResponseProtoMessage { }; class SelectCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 54; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 54; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "select_command_request"; } #endif @@ -1572,8 +1572,8 @@ class SelectCommandRequest : public CommandProtoMessage { #ifdef USE_SIREN class ListEntitiesSirenResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 55; - static constexpr uint16_t ESTIMATED_SIZE = 71; + static constexpr uint8_t MESSAGE_TYPE = 55; + static constexpr uint8_t ESTIMATED_SIZE = 71; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_siren_response"; } #endif @@ -1593,8 +1593,8 @@ class ListEntitiesSirenResponse : public InfoResponseProtoMessage { }; class SirenStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 56; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 56; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "siren_state_response"; } #endif @@ -1611,8 +1611,8 @@ class SirenStateResponse : public StateResponseProtoMessage { }; class SirenCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 57; - static constexpr uint16_t ESTIMATED_SIZE = 37; + static constexpr uint8_t MESSAGE_TYPE = 57; + static constexpr uint8_t ESTIMATED_SIZE = 37; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "siren_command_request"; } #endif @@ -1639,8 +1639,8 @@ class SirenCommandRequest : public CommandProtoMessage { #ifdef USE_LOCK class ListEntitiesLockResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 58; - static constexpr uint16_t ESTIMATED_SIZE = 64; + static constexpr uint8_t MESSAGE_TYPE = 58; + static constexpr uint8_t ESTIMATED_SIZE = 64; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_lock_response"; } #endif @@ -1661,8 +1661,8 @@ class ListEntitiesLockResponse : public InfoResponseProtoMessage { }; class LockStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 59; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 59; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "lock_state_response"; } #endif @@ -1679,8 +1679,8 @@ class LockStateResponse : public StateResponseProtoMessage { }; class LockCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 60; - static constexpr uint16_t ESTIMATED_SIZE = 22; + static constexpr uint8_t MESSAGE_TYPE = 60; + static constexpr uint8_t ESTIMATED_SIZE = 22; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "lock_command_request"; } #endif @@ -1702,8 +1702,8 @@ class LockCommandRequest : public CommandProtoMessage { #ifdef USE_BUTTON class ListEntitiesButtonResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 61; - static constexpr uint16_t ESTIMATED_SIZE = 58; + static constexpr uint8_t MESSAGE_TYPE = 61; + static constexpr uint8_t ESTIMATED_SIZE = 58; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_button_response"; } #endif @@ -1721,8 +1721,8 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage { }; class ButtonCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 62; - static constexpr uint16_t ESTIMATED_SIZE = 9; + static constexpr uint8_t MESSAGE_TYPE = 62; + static constexpr uint8_t ESTIMATED_SIZE = 9; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "button_command_request"; } #endif @@ -1757,8 +1757,8 @@ class MediaPlayerSupportedFormat : public ProtoMessage { }; class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 63; - static constexpr uint16_t ESTIMATED_SIZE = 85; + static constexpr uint8_t MESSAGE_TYPE = 63; + static constexpr uint8_t ESTIMATED_SIZE = 85; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_media_player_response"; } #endif @@ -1777,8 +1777,8 @@ class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { }; class MediaPlayerStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 64; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 64; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "media_player_state_response"; } #endif @@ -1797,8 +1797,8 @@ class MediaPlayerStateResponse : public StateResponseProtoMessage { }; class MediaPlayerCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 65; - static constexpr uint16_t ESTIMATED_SIZE = 35; + static constexpr uint8_t MESSAGE_TYPE = 65; + static constexpr uint8_t ESTIMATED_SIZE = 35; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "media_player_command_request"; } #endif @@ -1825,8 +1825,8 @@ class MediaPlayerCommandRequest : public CommandProtoMessage { #ifdef USE_BLUETOOTH_PROXY class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 66; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 66; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_bluetooth_le_advertisements_request"; } #endif @@ -1857,8 +1857,8 @@ class BluetoothServiceData : public ProtoMessage { }; class BluetoothLEAdvertisementResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 67; - static constexpr uint16_t ESTIMATED_SIZE = 107; + static constexpr uint8_t MESSAGE_TYPE = 67; + static constexpr uint8_t ESTIMATED_SIZE = 107; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_le_advertisement_response"; } #endif @@ -1897,8 +1897,8 @@ class BluetoothLERawAdvertisement : public ProtoMessage { }; class BluetoothLERawAdvertisementsResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 93; - static constexpr uint16_t ESTIMATED_SIZE = 34; + static constexpr uint8_t MESSAGE_TYPE = 93; + static constexpr uint8_t ESTIMATED_SIZE = 34; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_le_raw_advertisements_response"; } #endif @@ -1914,8 +1914,8 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage { }; class BluetoothDeviceRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 68; - static constexpr uint16_t ESTIMATED_SIZE = 12; + static constexpr uint8_t MESSAGE_TYPE = 68; + static constexpr uint8_t ESTIMATED_SIZE = 12; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_device_request"; } #endif @@ -1934,8 +1934,8 @@ class BluetoothDeviceRequest : public ProtoMessage { }; class BluetoothDeviceConnectionResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 69; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint8_t MESSAGE_TYPE = 69; + static constexpr uint8_t ESTIMATED_SIZE = 14; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_device_connection_response"; } #endif @@ -1954,8 +1954,8 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage { }; class BluetoothGATTGetServicesRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 70; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 70; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_get_services_request"; } #endif @@ -2015,8 +2015,8 @@ class BluetoothGATTService : public ProtoMessage { }; class BluetoothGATTGetServicesResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 71; - static constexpr uint16_t ESTIMATED_SIZE = 38; + static constexpr uint8_t MESSAGE_TYPE = 71; + static constexpr uint8_t ESTIMATED_SIZE = 38; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_get_services_response"; } #endif @@ -2034,8 +2034,8 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage { }; class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 72; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 72; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_get_services_done_response"; } #endif @@ -2051,8 +2051,8 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { }; class BluetoothGATTReadRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 73; - static constexpr uint16_t ESTIMATED_SIZE = 8; + static constexpr uint8_t MESSAGE_TYPE = 73; + static constexpr uint8_t ESTIMATED_SIZE = 8; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_read_request"; } #endif @@ -2069,8 +2069,8 @@ class BluetoothGATTReadRequest : public ProtoMessage { }; class BluetoothGATTReadResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 74; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint8_t MESSAGE_TYPE = 74; + static constexpr uint8_t ESTIMATED_SIZE = 17; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_read_response"; } #endif @@ -2089,8 +2089,8 @@ class BluetoothGATTReadResponse : public ProtoMessage { }; class BluetoothGATTWriteRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 75; - static constexpr uint16_t ESTIMATED_SIZE = 19; + static constexpr uint8_t MESSAGE_TYPE = 75; + static constexpr uint8_t ESTIMATED_SIZE = 19; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_write_request"; } #endif @@ -2110,8 +2110,8 @@ class BluetoothGATTWriteRequest : public ProtoMessage { }; class BluetoothGATTReadDescriptorRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 76; - static constexpr uint16_t ESTIMATED_SIZE = 8; + static constexpr uint8_t MESSAGE_TYPE = 76; + static constexpr uint8_t ESTIMATED_SIZE = 8; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_read_descriptor_request"; } #endif @@ -2128,8 +2128,8 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage { }; class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 77; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint8_t MESSAGE_TYPE = 77; + static constexpr uint8_t ESTIMATED_SIZE = 17; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_write_descriptor_request"; } #endif @@ -2148,8 +2148,8 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { }; class BluetoothGATTNotifyRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 78; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint8_t MESSAGE_TYPE = 78; + static constexpr uint8_t ESTIMATED_SIZE = 10; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_notify_request"; } #endif @@ -2167,8 +2167,8 @@ class BluetoothGATTNotifyRequest : public ProtoMessage { }; class BluetoothGATTNotifyDataResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 79; - static constexpr uint16_t ESTIMATED_SIZE = 17; + static constexpr uint8_t MESSAGE_TYPE = 79; + static constexpr uint8_t ESTIMATED_SIZE = 17; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_notify_data_response"; } #endif @@ -2187,8 +2187,8 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage { }; class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 80; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 80; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_bluetooth_connections_free_request"; } #endif @@ -2200,8 +2200,8 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { }; class BluetoothConnectionsFreeResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 81; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint8_t MESSAGE_TYPE = 81; + static constexpr uint8_t ESTIMATED_SIZE = 16; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_connections_free_response"; } #endif @@ -2219,8 +2219,8 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { }; class BluetoothGATTErrorResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 82; - static constexpr uint16_t ESTIMATED_SIZE = 12; + static constexpr uint8_t MESSAGE_TYPE = 82; + static constexpr uint8_t ESTIMATED_SIZE = 12; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_error_response"; } #endif @@ -2238,8 +2238,8 @@ class BluetoothGATTErrorResponse : public ProtoMessage { }; class BluetoothGATTWriteResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 83; - static constexpr uint16_t ESTIMATED_SIZE = 8; + static constexpr uint8_t MESSAGE_TYPE = 83; + static constexpr uint8_t ESTIMATED_SIZE = 8; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_write_response"; } #endif @@ -2256,8 +2256,8 @@ class BluetoothGATTWriteResponse : public ProtoMessage { }; class BluetoothGATTNotifyResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 84; - static constexpr uint16_t ESTIMATED_SIZE = 8; + static constexpr uint8_t MESSAGE_TYPE = 84; + static constexpr uint8_t ESTIMATED_SIZE = 8; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_gatt_notify_response"; } #endif @@ -2274,8 +2274,8 @@ class BluetoothGATTNotifyResponse : public ProtoMessage { }; class BluetoothDevicePairingResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 85; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint8_t MESSAGE_TYPE = 85; + static constexpr uint8_t ESTIMATED_SIZE = 10; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_device_pairing_response"; } #endif @@ -2293,8 +2293,8 @@ class BluetoothDevicePairingResponse : public ProtoMessage { }; class BluetoothDeviceUnpairingResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 86; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint8_t MESSAGE_TYPE = 86; + static constexpr uint8_t ESTIMATED_SIZE = 10; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_device_unpairing_response"; } #endif @@ -2312,8 +2312,8 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { }; class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 87; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 87; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "unsubscribe_bluetooth_le_advertisements_request"; } #endif @@ -2325,8 +2325,8 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { }; class BluetoothDeviceClearCacheResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 88; - static constexpr uint16_t ESTIMATED_SIZE = 10; + static constexpr uint8_t MESSAGE_TYPE = 88; + static constexpr uint8_t ESTIMATED_SIZE = 10; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_device_clear_cache_response"; } #endif @@ -2344,8 +2344,8 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage { }; class BluetoothScannerStateResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 126; - static constexpr uint16_t ESTIMATED_SIZE = 4; + static constexpr uint8_t MESSAGE_TYPE = 126; + static constexpr uint8_t ESTIMATED_SIZE = 4; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_scanner_state_response"; } #endif @@ -2362,8 +2362,8 @@ class BluetoothScannerStateResponse : public ProtoMessage { }; class BluetoothScannerSetModeRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 127; - static constexpr uint16_t ESTIMATED_SIZE = 2; + static constexpr uint8_t MESSAGE_TYPE = 127; + static constexpr uint8_t ESTIMATED_SIZE = 2; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "bluetooth_scanner_set_mode_request"; } #endif @@ -2381,8 +2381,8 @@ class BluetoothScannerSetModeRequest : public ProtoMessage { #ifdef USE_VOICE_ASSISTANT class SubscribeVoiceAssistantRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 89; - static constexpr uint16_t ESTIMATED_SIZE = 6; + static constexpr uint8_t MESSAGE_TYPE = 89; + static constexpr uint8_t ESTIMATED_SIZE = 6; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "subscribe_voice_assistant_request"; } #endif @@ -2414,8 +2414,8 @@ class VoiceAssistantAudioSettings : public ProtoMessage { }; class VoiceAssistantRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 90; - static constexpr uint16_t ESTIMATED_SIZE = 41; + static constexpr uint8_t MESSAGE_TYPE = 90; + static constexpr uint8_t ESTIMATED_SIZE = 41; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_request"; } #endif @@ -2436,8 +2436,8 @@ class VoiceAssistantRequest : public ProtoMessage { }; class VoiceAssistantResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 91; - static constexpr uint16_t ESTIMATED_SIZE = 6; + static constexpr uint8_t MESSAGE_TYPE = 91; + static constexpr uint8_t ESTIMATED_SIZE = 6; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_response"; } #endif @@ -2467,8 +2467,8 @@ class VoiceAssistantEventData : public ProtoMessage { }; class VoiceAssistantEventResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 92; - static constexpr uint16_t ESTIMATED_SIZE = 36; + static constexpr uint8_t MESSAGE_TYPE = 92; + static constexpr uint8_t ESTIMATED_SIZE = 36; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_event_response"; } #endif @@ -2486,8 +2486,8 @@ class VoiceAssistantEventResponse : public ProtoMessage { }; class VoiceAssistantAudio : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 106; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 106; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_audio"; } #endif @@ -2505,8 +2505,8 @@ class VoiceAssistantAudio : public ProtoMessage { }; class VoiceAssistantTimerEventResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 115; - static constexpr uint16_t ESTIMATED_SIZE = 30; + static constexpr uint8_t MESSAGE_TYPE = 115; + static constexpr uint8_t ESTIMATED_SIZE = 30; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_timer_event_response"; } #endif @@ -2528,8 +2528,8 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage { }; class VoiceAssistantAnnounceRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 119; - static constexpr uint16_t ESTIMATED_SIZE = 29; + static constexpr uint8_t MESSAGE_TYPE = 119; + static constexpr uint8_t ESTIMATED_SIZE = 29; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_announce_request"; } #endif @@ -2549,8 +2549,8 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { }; class VoiceAssistantAnnounceFinished : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 120; - static constexpr uint16_t ESTIMATED_SIZE = 2; + static constexpr uint8_t MESSAGE_TYPE = 120; + static constexpr uint8_t ESTIMATED_SIZE = 2; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_announce_finished"; } #endif @@ -2580,8 +2580,8 @@ class VoiceAssistantWakeWord : public ProtoMessage { }; class VoiceAssistantConfigurationRequest : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 121; - static constexpr uint16_t ESTIMATED_SIZE = 0; + static constexpr uint8_t MESSAGE_TYPE = 121; + static constexpr uint8_t ESTIMATED_SIZE = 0; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_configuration_request"; } #endif @@ -2593,8 +2593,8 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage { }; class VoiceAssistantConfigurationResponse : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 122; - static constexpr uint16_t ESTIMATED_SIZE = 56; + static constexpr uint8_t MESSAGE_TYPE = 122; + static constexpr uint8_t ESTIMATED_SIZE = 56; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_configuration_response"; } #endif @@ -2613,8 +2613,8 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 123; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 123; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "voice_assistant_set_configuration"; } #endif @@ -2632,8 +2632,8 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { #ifdef USE_ALARM_CONTROL_PANEL class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 94; - static constexpr uint16_t ESTIMATED_SIZE = 57; + static constexpr uint8_t MESSAGE_TYPE = 94; + static constexpr uint8_t ESTIMATED_SIZE = 57; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_alarm_control_panel_response"; } #endif @@ -2653,8 +2653,8 @@ class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { }; class AlarmControlPanelStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 95; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 95; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "alarm_control_panel_state_response"; } #endif @@ -2671,8 +2671,8 @@ class AlarmControlPanelStateResponse : public StateResponseProtoMessage { }; class AlarmControlPanelCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 96; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 96; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "alarm_control_panel_command_request"; } #endif @@ -2693,8 +2693,8 @@ class AlarmControlPanelCommandRequest : public CommandProtoMessage { #ifdef USE_TEXT class ListEntitiesTextResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 97; - static constexpr uint16_t ESTIMATED_SIZE = 68; + static constexpr uint8_t MESSAGE_TYPE = 97; + static constexpr uint8_t ESTIMATED_SIZE = 68; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_text_response"; } #endif @@ -2715,8 +2715,8 @@ class ListEntitiesTextResponse : public InfoResponseProtoMessage { }; class TextStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 98; - static constexpr uint16_t ESTIMATED_SIZE = 20; + static constexpr uint8_t MESSAGE_TYPE = 98; + static constexpr uint8_t ESTIMATED_SIZE = 20; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "text_state_response"; } #endif @@ -2735,8 +2735,8 @@ class TextStateResponse : public StateResponseProtoMessage { }; class TextCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 99; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 99; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "text_command_request"; } #endif @@ -2756,8 +2756,8 @@ class TextCommandRequest : public CommandProtoMessage { #ifdef USE_DATETIME_DATE class ListEntitiesDateResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 100; - static constexpr uint16_t ESTIMATED_SIZE = 49; + static constexpr uint8_t MESSAGE_TYPE = 100; + static constexpr uint8_t ESTIMATED_SIZE = 49; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_date_response"; } #endif @@ -2774,8 +2774,8 @@ class ListEntitiesDateResponse : public InfoResponseProtoMessage { }; class DateStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 101; - static constexpr uint16_t ESTIMATED_SIZE = 23; + static constexpr uint8_t MESSAGE_TYPE = 101; + static constexpr uint8_t ESTIMATED_SIZE = 23; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_state_response"; } #endif @@ -2795,8 +2795,8 @@ class DateStateResponse : public StateResponseProtoMessage { }; class DateCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 102; - static constexpr uint16_t ESTIMATED_SIZE = 21; + static constexpr uint8_t MESSAGE_TYPE = 102; + static constexpr uint8_t ESTIMATED_SIZE = 21; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_command_request"; } #endif @@ -2817,8 +2817,8 @@ class DateCommandRequest : public CommandProtoMessage { #ifdef USE_DATETIME_TIME class ListEntitiesTimeResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 103; - static constexpr uint16_t ESTIMATED_SIZE = 49; + static constexpr uint8_t MESSAGE_TYPE = 103; + static constexpr uint8_t ESTIMATED_SIZE = 49; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_time_response"; } #endif @@ -2835,8 +2835,8 @@ class ListEntitiesTimeResponse : public InfoResponseProtoMessage { }; class TimeStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 104; - static constexpr uint16_t ESTIMATED_SIZE = 23; + static constexpr uint8_t MESSAGE_TYPE = 104; + static constexpr uint8_t ESTIMATED_SIZE = 23; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "time_state_response"; } #endif @@ -2856,8 +2856,8 @@ class TimeStateResponse : public StateResponseProtoMessage { }; class TimeCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 105; - static constexpr uint16_t ESTIMATED_SIZE = 21; + static constexpr uint8_t MESSAGE_TYPE = 105; + static constexpr uint8_t ESTIMATED_SIZE = 21; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "time_command_request"; } #endif @@ -2878,8 +2878,8 @@ class TimeCommandRequest : public CommandProtoMessage { #ifdef USE_EVENT class ListEntitiesEventResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 107; - static constexpr uint16_t ESTIMATED_SIZE = 76; + static constexpr uint8_t MESSAGE_TYPE = 107; + static constexpr uint8_t ESTIMATED_SIZE = 76; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_event_response"; } #endif @@ -2898,8 +2898,8 @@ class ListEntitiesEventResponse : public InfoResponseProtoMessage { }; class EventResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 108; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 108; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "event_response"; } #endif @@ -2919,8 +2919,8 @@ class EventResponse : public StateResponseProtoMessage { #ifdef USE_VALVE class ListEntitiesValveResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 109; - static constexpr uint16_t ESTIMATED_SIZE = 64; + static constexpr uint8_t MESSAGE_TYPE = 109; + static constexpr uint8_t ESTIMATED_SIZE = 64; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_valve_response"; } #endif @@ -2941,8 +2941,8 @@ class ListEntitiesValveResponse : public InfoResponseProtoMessage { }; class ValveStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 110; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint8_t MESSAGE_TYPE = 110; + static constexpr uint8_t ESTIMATED_SIZE = 16; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "valve_state_response"; } #endif @@ -2960,8 +2960,8 @@ class ValveStateResponse : public StateResponseProtoMessage { }; class ValveCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 111; - static constexpr uint16_t ESTIMATED_SIZE = 18; + static constexpr uint8_t MESSAGE_TYPE = 111; + static constexpr uint8_t ESTIMATED_SIZE = 18; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "valve_command_request"; } #endif @@ -2982,8 +2982,8 @@ class ValveCommandRequest : public CommandProtoMessage { #ifdef USE_DATETIME_DATETIME class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 112; - static constexpr uint16_t ESTIMATED_SIZE = 49; + static constexpr uint8_t MESSAGE_TYPE = 112; + static constexpr uint8_t ESTIMATED_SIZE = 49; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_date_time_response"; } #endif @@ -3000,8 +3000,8 @@ class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { }; class DateTimeStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 113; - static constexpr uint16_t ESTIMATED_SIZE = 16; + static constexpr uint8_t MESSAGE_TYPE = 113; + static constexpr uint8_t ESTIMATED_SIZE = 16; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_time_state_response"; } #endif @@ -3019,8 +3019,8 @@ class DateTimeStateResponse : public StateResponseProtoMessage { }; class DateTimeCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 114; - static constexpr uint16_t ESTIMATED_SIZE = 14; + static constexpr uint8_t MESSAGE_TYPE = 114; + static constexpr uint8_t ESTIMATED_SIZE = 14; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "date_time_command_request"; } #endif @@ -3039,8 +3039,8 @@ class DateTimeCommandRequest : public CommandProtoMessage { #ifdef USE_UPDATE class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 116; - static constexpr uint16_t ESTIMATED_SIZE = 58; + static constexpr uint8_t MESSAGE_TYPE = 116; + static constexpr uint8_t ESTIMATED_SIZE = 58; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_update_response"; } #endif @@ -3058,8 +3058,8 @@ class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { }; class UpdateStateResponse : public StateResponseProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 117; - static constexpr uint16_t ESTIMATED_SIZE = 65; + static constexpr uint8_t MESSAGE_TYPE = 117; + static constexpr uint8_t ESTIMATED_SIZE = 65; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "update_state_response"; } #endif @@ -3085,8 +3085,8 @@ class UpdateStateResponse : public StateResponseProtoMessage { }; class UpdateCommandRequest : public CommandProtoMessage { public: - static constexpr uint16_t MESSAGE_TYPE = 118; - static constexpr uint16_t ESTIMATED_SIZE = 11; + static constexpr uint8_t MESSAGE_TYPE = 118; + static constexpr uint8_t ESTIMATED_SIZE = 11; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "update_command_request"; } #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 0915746381..6a5d273ec1 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -475,7 +475,8 @@ void APIServer::on_shutdown() { if (!c->send_message(DisconnectRequest())) { // If we can't send the disconnect request directly (tx_buffer full), // schedule it at the front of the batch so it will be sent with priority - c->schedule_message_front_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE); + c->schedule_message_front_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE, + DisconnectRequest::ESTIMATED_SIZE); } } } diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 4c83ca0935..5e6074e008 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -14,7 +14,7 @@ class APIConnection; #define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \ bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \ return this->client_->schedule_message_(entity, &APIConnection::try_send_##entity_type##_info, \ - ResponseType::MESSAGE_TYPE); \ + ResponseType::MESSAGE_TYPE, ResponseType::ESTIMATED_SIZE); \ } class ListEntitiesIterator : public ComponentIterator { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 764bac2f39..2271ba7dbd 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -363,11 +363,11 @@ class ProtoService { * @return A ProtoWriteBuffer object with the reserved size. */ virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0; - virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0; + virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0; virtual void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; // Optimized method that pre-allocates buffer based on message size - bool send_message_(const ProtoMessage &msg, uint16_t message_type) { + bool send_message_(const ProtoMessage &msg, uint8_t message_type) { uint32_t msg_size = 0; msg.calculate_size(msg_size); diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index df1f3f8caa..c663af0a5f 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -987,13 +987,24 @@ def build_message_type( # Add MESSAGE_TYPE method if this is a service message if message_id is not None: + # Validate that message_id fits in uint8_t + if message_id > 255: + raise ValueError( + f"Message ID {message_id} for {desc.name} exceeds uint8_t maximum (255)" + ) + # Add static constexpr for message type - public_content.append(f"static constexpr uint16_t MESSAGE_TYPE = {message_id};") + public_content.append(f"static constexpr uint8_t MESSAGE_TYPE = {message_id};") # Add estimated size constant estimated_size = calculate_message_estimated_size(desc) + # Validate that estimated_size fits in uint8_t + if estimated_size > 255: + raise ValueError( + f"Estimated size {estimated_size} for {desc.name} exceeds uint8_t maximum (255)" + ) public_content.append( - f"static constexpr uint16_t ESTIMATED_SIZE = {estimated_size};" + f"static constexpr uint8_t ESTIMATED_SIZE = {estimated_size};" ) # Add message_name method inline in header From 7a6894e087c6b6de0bdc99fe157e643d2775bb68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 16:08:52 -1000 Subject: [PATCH 12/90] Optimize API proto size calculations by removing redundant force parameter (#9449) --- esphome/components/api/api_pb2.cpp | 1408 ++++++++++++------------- esphome/components/api/api_pb2_size.h | 198 +++- script/api_protobuf/api_protobuf.py | 151 ++- 3 files changed, 953 insertions(+), 804 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index af82299f53..0c110b8c8b 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -38,9 +38,9 @@ void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->api_version_minor); } void HelloRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->client_info, false); - ProtoSize::add_uint32_field(total_size, 1, this->api_version_major, false); - ProtoSize::add_uint32_field(total_size, 1, this->api_version_minor, false); + ProtoSize::add_string_field(total_size, 1, this->client_info); + ProtoSize::add_uint32_field(total_size, 1, this->api_version_major); + ProtoSize::add_uint32_field(total_size, 1, this->api_version_minor); } bool HelloResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -77,10 +77,10 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->name); } void HelloResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->api_version_major, false); - ProtoSize::add_uint32_field(total_size, 1, this->api_version_minor, false); - ProtoSize::add_string_field(total_size, 1, this->server_info, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); + ProtoSize::add_uint32_field(total_size, 1, this->api_version_major); + ProtoSize::add_uint32_field(total_size, 1, this->api_version_minor); + ProtoSize::add_string_field(total_size, 1, this->server_info); + ProtoSize::add_string_field(total_size, 1, this->name); } bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -94,7 +94,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value } void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); } void ConnectRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->password, false); + ProtoSize::add_string_field(total_size, 1, this->password); } bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -108,7 +108,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { } void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); } void ConnectResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->invalid_password, false); + ProtoSize::add_bool_field(total_size, 1, this->invalid_password); } bool AreaInfo::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -135,8 +135,8 @@ void AreaInfo::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(2, this->name); } void AreaInfo::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->area_id, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); + ProtoSize::add_uint32_field(total_size, 1, this->area_id); + ProtoSize::add_string_field(total_size, 1, this->name); } bool DeviceInfo::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -168,9 +168,9 @@ void DeviceInfo::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->area_id); } void DeviceInfo::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_uint32_field(total_size, 1, this->area_id, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_uint32_field(total_size, 1, this->area_id); } bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -301,28 +301,28 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_message(22, this->area); } void DeviceInfoResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->uses_password, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->mac_address, false); - ProtoSize::add_string_field(total_size, 1, this->esphome_version, false); - ProtoSize::add_string_field(total_size, 1, this->compilation_time, false); - ProtoSize::add_string_field(total_size, 1, this->model, false); - ProtoSize::add_bool_field(total_size, 1, this->has_deep_sleep, false); - ProtoSize::add_string_field(total_size, 1, this->project_name, false); - ProtoSize::add_string_field(total_size, 1, this->project_version, false); - ProtoSize::add_uint32_field(total_size, 1, this->webserver_port, false); - ProtoSize::add_uint32_field(total_size, 1, this->legacy_bluetooth_proxy_version, false); - ProtoSize::add_uint32_field(total_size, 1, this->bluetooth_proxy_feature_flags, false); - ProtoSize::add_string_field(total_size, 1, this->manufacturer, false); - ProtoSize::add_string_field(total_size, 1, this->friendly_name, false); - ProtoSize::add_uint32_field(total_size, 1, this->legacy_voice_assistant_version, false); - ProtoSize::add_uint32_field(total_size, 2, this->voice_assistant_feature_flags, false); - ProtoSize::add_string_field(total_size, 2, this->suggested_area, false); - ProtoSize::add_string_field(total_size, 2, this->bluetooth_mac_address, false); - ProtoSize::add_bool_field(total_size, 2, this->api_encryption_supported, false); + ProtoSize::add_bool_field(total_size, 1, this->uses_password); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->mac_address); + ProtoSize::add_string_field(total_size, 1, this->esphome_version); + ProtoSize::add_string_field(total_size, 1, this->compilation_time); + ProtoSize::add_string_field(total_size, 1, this->model); + ProtoSize::add_bool_field(total_size, 1, this->has_deep_sleep); + ProtoSize::add_string_field(total_size, 1, this->project_name); + ProtoSize::add_string_field(total_size, 1, this->project_version); + ProtoSize::add_uint32_field(total_size, 1, this->webserver_port); + ProtoSize::add_uint32_field(total_size, 1, this->legacy_bluetooth_proxy_version); + ProtoSize::add_uint32_field(total_size, 1, this->bluetooth_proxy_feature_flags); + ProtoSize::add_string_field(total_size, 1, this->manufacturer); + ProtoSize::add_string_field(total_size, 1, this->friendly_name); + ProtoSize::add_uint32_field(total_size, 1, this->legacy_voice_assistant_version); + ProtoSize::add_uint32_field(total_size, 2, this->voice_assistant_feature_flags); + ProtoSize::add_string_field(total_size, 2, this->suggested_area); + ProtoSize::add_string_field(total_size, 2, this->bluetooth_mac_address); + ProtoSize::add_bool_field(total_size, 2, this->api_encryption_supported); ProtoSize::add_repeated_message(total_size, 2, this->devices); ProtoSize::add_repeated_message(total_size, 2, this->areas); - ProtoSize::add_message_object(total_size, 2, this->area, false); + ProtoSize::add_message_object(total_size, 2, this->area); } #ifdef USE_BINARY_SENSOR bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -396,16 +396,16 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_bool_field(total_size, 1, this->is_status_binary_sensor, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_bool_field(total_size, 1, this->is_status_binary_sensor); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -442,10 +442,10 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void BinarySensorStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_COVER @@ -535,19 +535,19 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(13, this->device_id); } void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_position, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_tilt, false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_bool_field(total_size, 1, this->assumed_state); + ProtoSize::add_bool_field(total_size, 1, this->supports_position); + ProtoSize::add_bool_field(total_size, 1, this->supports_tilt); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_bool_field(total_size, 1, this->supports_stop); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -594,12 +594,12 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(6, this->device_id); } void CoverStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_state), false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->current_operation), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_state)); + ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->current_operation)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -661,15 +661,15 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(9, this->device_id); } void CoverCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_legacy_command, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_command), false); - ProtoSize::add_bool_field(total_size, 1, this->has_position, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_tilt, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->stop, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_legacy_command); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_command)); + ProtoSize::add_bool_field(total_size, 1, this->has_position); + ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_tilt); + ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->stop); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_FAN @@ -761,23 +761,23 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(13, this->device_id); } void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_oscillation, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_speed, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_direction, false); - ProtoSize::add_int32_field(total_size, 1, this->supported_speed_count, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_bool_field(total_size, 1, this->supports_oscillation); + ProtoSize::add_bool_field(total_size, 1, this->supports_speed); + ProtoSize::add_bool_field(total_size, 1, this->supports_direction); + ProtoSize::add_int32_field(total_size, 1, this->supported_speed_count); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); if (!this->supported_preset_modes.empty()) { for (const auto &it : this->supported_preset_modes) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -840,14 +840,14 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(8, this->device_id); } void FanStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->oscillating, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->speed), false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->direction), false); - ProtoSize::add_int32_field(total_size, 1, this->speed_level, false); - ProtoSize::add_string_field(total_size, 1, this->preset_mode, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->oscillating); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->speed)); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->direction)); + ProtoSize::add_int32_field(total_size, 1, this->speed_level); + ProtoSize::add_string_field(total_size, 1, this->preset_mode); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -940,20 +940,20 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(14, this->device_id); } void FanCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_state, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->has_speed, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->speed), false); - ProtoSize::add_bool_field(total_size, 1, this->has_oscillating, false); - ProtoSize::add_bool_field(total_size, 1, this->oscillating, false); - ProtoSize::add_bool_field(total_size, 1, this->has_direction, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->direction), false); - ProtoSize::add_bool_field(total_size, 1, this->has_speed_level, false); - ProtoSize::add_int32_field(total_size, 1, this->speed_level, false); - ProtoSize::add_bool_field(total_size, 1, this->has_preset_mode, false); - ProtoSize::add_string_field(total_size, 1, this->preset_mode, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_state); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->has_speed); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->speed)); + ProtoSize::add_bool_field(total_size, 1, this->has_oscillating); + ProtoSize::add_bool_field(total_size, 1, this->oscillating); + ProtoSize::add_bool_field(total_size, 1, this->has_direction); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->direction)); + ProtoSize::add_bool_field(total_size, 1, this->has_speed_level); + ProtoSize::add_int32_field(total_size, 1, this->speed_level); + ProtoSize::add_bool_field(total_size, 1, this->has_preset_mode); + ProtoSize::add_string_field(total_size, 1, this->preset_mode); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_LIGHT @@ -1062,30 +1062,30 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(16, this->device_id); } void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); if (!this->supported_color_modes.empty()) { for (const auto &it : this->supported_color_modes) { - ProtoSize::add_enum_field(total_size, 1, static_cast(it), true); + ProtoSize::add_enum_field_repeated(total_size, 1, static_cast(it)); } } - ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_brightness, false); - ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_rgb, false); - ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_white_value, false); - ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_color_temperature, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->min_mireds != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->max_mireds != 0.0f, false); + ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_brightness); + ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_rgb); + ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_white_value); + ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_color_temperature); + ProtoSize::add_fixed_field<4>(total_size, 1, this->min_mireds != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->max_mireds != 0.0f); if (!this->effects.empty()) { for (const auto &it : this->effects) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 2, this->device_id); } bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1178,20 +1178,20 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(14, this->device_id); } void LightStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->brightness != 0.0f, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->color_mode), false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->color_brightness != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->red != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->green != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->blue != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->white != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->color_temperature != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->cold_white != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->warm_white != 0.0f, false); - ProtoSize::add_string_field(total_size, 1, this->effect, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_fixed_field<4>(total_size, 1, this->brightness != 0.0f); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->color_mode)); + ProtoSize::add_fixed_field<4>(total_size, 1, this->color_brightness != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->red != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->green != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->blue != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->white != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->color_temperature != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->cold_white != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->warm_white != 0.0f); + ProtoSize::add_string_field(total_size, 1, this->effect); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1354,34 +1354,34 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(28, this->device_id); } void LightCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_state, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->has_brightness, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->brightness != 0.0f, false); - ProtoSize::add_bool_field(total_size, 2, this->has_color_mode, false); - ProtoSize::add_enum_field(total_size, 2, static_cast(this->color_mode), false); - ProtoSize::add_bool_field(total_size, 2, this->has_color_brightness, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->color_brightness != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_rgb, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->red != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->green != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->blue != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_white, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->white != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_color_temperature, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->color_temperature != 0.0f, false); - ProtoSize::add_bool_field(total_size, 2, this->has_cold_white, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->cold_white != 0.0f, false); - ProtoSize::add_bool_field(total_size, 2, this->has_warm_white, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->warm_white != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_transition_length, false); - ProtoSize::add_uint32_field(total_size, 1, this->transition_length, false); - ProtoSize::add_bool_field(total_size, 2, this->has_flash_length, false); - ProtoSize::add_uint32_field(total_size, 2, this->flash_length, false); - ProtoSize::add_bool_field(total_size, 2, this->has_effect, false); - ProtoSize::add_string_field(total_size, 2, this->effect, false); - ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_state); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->has_brightness); + ProtoSize::add_fixed_field<4>(total_size, 1, this->brightness != 0.0f); + ProtoSize::add_bool_field(total_size, 2, this->has_color_mode); + ProtoSize::add_enum_field(total_size, 2, static_cast(this->color_mode)); + ProtoSize::add_bool_field(total_size, 2, this->has_color_brightness); + ProtoSize::add_fixed_field<4>(total_size, 2, this->color_brightness != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_rgb); + ProtoSize::add_fixed_field<4>(total_size, 1, this->red != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->green != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->blue != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_white); + ProtoSize::add_fixed_field<4>(total_size, 1, this->white != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_color_temperature); + ProtoSize::add_fixed_field<4>(total_size, 1, this->color_temperature != 0.0f); + ProtoSize::add_bool_field(total_size, 2, this->has_cold_white); + ProtoSize::add_fixed_field<4>(total_size, 2, this->cold_white != 0.0f); + ProtoSize::add_bool_field(total_size, 2, this->has_warm_white); + ProtoSize::add_fixed_field<4>(total_size, 2, this->warm_white != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_transition_length); + ProtoSize::add_uint32_field(total_size, 1, this->transition_length); + ProtoSize::add_bool_field(total_size, 2, this->has_flash_length); + ProtoSize::add_uint32_field(total_size, 2, this->flash_length); + ProtoSize::add_bool_field(total_size, 2, this->has_effect); + ProtoSize::add_string_field(total_size, 2, this->effect); + ProtoSize::add_uint32_field(total_size, 2, this->device_id); } #endif #ifdef USE_SENSOR @@ -1476,20 +1476,20 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(14, this->device_id); } void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement, false); - ProtoSize::add_int32_field(total_size, 1, this->accuracy_decimals, false); - ProtoSize::add_bool_field(total_size, 1, this->force_update, false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->state_class), false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_last_reset_type), false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement); + ProtoSize::add_int32_field(total_size, 1, this->accuracy_decimals); + ProtoSize::add_bool_field(total_size, 1, this->force_update); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->state_class)); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_last_reset_type)); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1526,10 +1526,10 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void SensorStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_SWITCH @@ -1604,16 +1604,16 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->assumed_state); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SwitchStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1645,9 +1645,9 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void SwitchStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SwitchCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1679,9 +1679,9 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void SwitchCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_TEXT_SENSOR @@ -1751,15 +1751,15 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(9, this->device_id); } void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1802,10 +1802,10 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void TextSensorStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -1827,8 +1827,8 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->dump_config); } void SubscribeLogsRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->level), false); - ProtoSize::add_bool_field(total_size, 1, this->dump_config, false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->level)); + ProtoSize::add_bool_field(total_size, 1, this->dump_config); } bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1860,9 +1860,9 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(4, this->send_failed); } void SubscribeLogsResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->level), false); - ProtoSize::add_string_field(total_size, 1, this->message, false); - ProtoSize::add_bool_field(total_size, 1, this->send_failed, false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->level)); + ProtoSize::add_string_field(total_size, 1, this->message); + ProtoSize::add_bool_field(total_size, 1, this->send_failed); } #ifdef USE_API_NOISE bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { @@ -1879,7 +1879,7 @@ void NoiseEncryptionSetKeyRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(1, reinterpret_cast(this->key.data()), this->key.size()); } void NoiseEncryptionSetKeyRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->key, false); + ProtoSize::add_string_field(total_size, 1, this->key); } bool NoiseEncryptionSetKeyResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1893,7 +1893,7 @@ bool NoiseEncryptionSetKeyResponse::decode_varint(uint32_t field_id, ProtoVarInt } void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); } void NoiseEncryptionSetKeyResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->success, false); + ProtoSize::add_bool_field(total_size, 1, this->success); } #endif bool HomeassistantServiceMap::decode_length(uint32_t field_id, ProtoLengthDelimited value) { @@ -1915,8 +1915,8 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(2, this->value); } void HomeassistantServiceMap::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->key, false); - ProtoSize::add_string_field(total_size, 1, this->value, false); + ProtoSize::add_string_field(total_size, 1, this->key); + ProtoSize::add_string_field(total_size, 1, this->value); } bool HomeassistantServiceResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -1964,11 +1964,11 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(5, this->is_event); } void HomeassistantServiceResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->service, false); + ProtoSize::add_string_field(total_size, 1, this->service); ProtoSize::add_repeated_message(total_size, 1, this->data); ProtoSize::add_repeated_message(total_size, 1, this->data_template); ProtoSize::add_repeated_message(total_size, 1, this->variables); - ProtoSize::add_bool_field(total_size, 1, this->is_event, false); + ProtoSize::add_bool_field(total_size, 1, this->is_event); } bool SubscribeHomeAssistantStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2000,9 +2000,9 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const buffer.encode_bool(3, this->once); } void SubscribeHomeAssistantStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->entity_id, false); - ProtoSize::add_string_field(total_size, 1, this->attribute, false); - ProtoSize::add_bool_field(total_size, 1, this->once, false); + ProtoSize::add_string_field(total_size, 1, this->entity_id); + ProtoSize::add_string_field(total_size, 1, this->attribute); + ProtoSize::add_bool_field(total_size, 1, this->once); } bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -2028,9 +2028,9 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(3, this->attribute); } void HomeAssistantStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->entity_id, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_string_field(total_size, 1, this->attribute, false); + ProtoSize::add_string_field(total_size, 1, this->entity_id); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_string_field(total_size, 1, this->attribute); } bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { switch (field_id) { @@ -2044,7 +2044,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { } void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); } void GetTimeResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0); } bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2071,8 +2071,8 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(2, this->type); } void ListEntitiesServicesArgument::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->type), false); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->type)); } bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -2106,8 +2106,8 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const { } } void ListEntitiesServicesResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); ProtoSize::add_repeated_message(total_size, 1, this->args); } bool ExecuteServiceArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -2184,29 +2184,27 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const { } } void ExecuteServiceArgument::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->bool_, false); - ProtoSize::add_int32_field(total_size, 1, this->legacy_int, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->float_ != 0.0f, false); - ProtoSize::add_string_field(total_size, 1, this->string_, false); - ProtoSize::add_sint32_field(total_size, 1, this->int_, false); + ProtoSize::add_bool_field(total_size, 1, this->bool_); + ProtoSize::add_int32_field(total_size, 1, this->legacy_int); + ProtoSize::add_fixed_field<4>(total_size, 1, this->float_ != 0.0f); + ProtoSize::add_string_field(total_size, 1, this->string_); + ProtoSize::add_sint32_field(total_size, 1, this->int_); if (!this->bool_array.empty()) { for (const auto it : this->bool_array) { - ProtoSize::add_bool_field(total_size, 1, it, true); + ProtoSize::add_bool_field_repeated(total_size, 1, it); } } if (!this->int_array.empty()) { for (const auto &it : this->int_array) { - ProtoSize::add_sint32_field(total_size, 1, it, true); + ProtoSize::add_sint32_field_repeated(total_size, 1, it); } } if (!this->float_array.empty()) { - for (const auto &it : this->float_array) { - ProtoSize::add_fixed_field<4>(total_size, 1, it != 0.0f, true); - } + total_size += this->float_array.size() * 5; } if (!this->string_array.empty()) { for (const auto &it : this->string_array) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } } @@ -2237,7 +2235,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const { } } void ExecuteServiceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); ProtoSize::add_repeated_message(total_size, 1, this->args); } #ifdef USE_CAMERA @@ -2302,14 +2300,14 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(8, this->device_id); } void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2352,10 +2350,10 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void CameraImageResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); - ProtoSize::add_bool_field(total_size, 1, this->done, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->data); + ProtoSize::add_bool_field(total_size, 1, this->done); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2376,8 +2374,8 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->stream); } void CameraImageRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->single, false); - ProtoSize::add_bool_field(total_size, 1, this->stream, false); + ProtoSize::add_bool_field(total_size, 1, this->single); + ProtoSize::add_bool_field(total_size, 1, this->stream); } #endif #ifdef USE_CLIMATE @@ -2544,56 +2542,56 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(26, this->device_id); } void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_current_temperature, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_two_point_target_temperature, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_bool_field(total_size, 1, this->supports_current_temperature); + ProtoSize::add_bool_field(total_size, 1, this->supports_two_point_target_temperature); if (!this->supported_modes.empty()) { for (const auto &it : this->supported_modes) { - ProtoSize::add_enum_field(total_size, 1, static_cast(it), true); + ProtoSize::add_enum_field_repeated(total_size, 1, static_cast(it)); } } - ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_min_temperature != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_max_temperature != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_target_temperature_step != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_away, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_action, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_min_temperature != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_max_temperature != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->visual_target_temperature_step != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_away); + ProtoSize::add_bool_field(total_size, 1, this->supports_action); if (!this->supported_fan_modes.empty()) { for (const auto &it : this->supported_fan_modes) { - ProtoSize::add_enum_field(total_size, 1, static_cast(it), true); + ProtoSize::add_enum_field_repeated(total_size, 1, static_cast(it)); } } if (!this->supported_swing_modes.empty()) { for (const auto &it : this->supported_swing_modes) { - ProtoSize::add_enum_field(total_size, 1, static_cast(it), true); + ProtoSize::add_enum_field_repeated(total_size, 1, static_cast(it)); } } if (!this->supported_custom_fan_modes.empty()) { for (const auto &it : this->supported_custom_fan_modes) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } if (!this->supported_presets.empty()) { for (const auto &it : this->supported_presets) { - ProtoSize::add_enum_field(total_size, 2, static_cast(it), true); + ProtoSize::add_enum_field_repeated(total_size, 2, static_cast(it)); } } if (!this->supported_custom_presets.empty()) { for (const auto &it : this->supported_custom_presets) { - ProtoSize::add_string_field(total_size, 2, it, true); + ProtoSize::add_string_field_repeated(total_size, 2, it); } } - ProtoSize::add_bool_field(total_size, 2, this->disabled_by_default, false); - ProtoSize::add_string_field(total_size, 2, this->icon, false); - ProtoSize::add_enum_field(total_size, 2, static_cast(this->entity_category), false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_current_temperature_step != 0.0f, false); - ProtoSize::add_bool_field(total_size, 2, this->supports_current_humidity, false); - ProtoSize::add_bool_field(total_size, 2, this->supports_target_humidity, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_min_humidity != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_max_humidity != 0.0f, false); - ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); + ProtoSize::add_bool_field(total_size, 2, this->disabled_by_default); + ProtoSize::add_string_field(total_size, 2, this->icon); + ProtoSize::add_enum_field(total_size, 2, static_cast(this->entity_category)); + ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_current_temperature_step != 0.0f); + ProtoSize::add_bool_field(total_size, 2, this->supports_current_humidity); + ProtoSize::add_bool_field(total_size, 2, this->supports_target_humidity); + ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_min_humidity != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_max_humidity != 0.0f); + ProtoSize::add_uint32_field(total_size, 2, this->device_id); } bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2696,22 +2694,22 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(16, this->device_id); } void ClimateStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->current_temperature != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_low != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_high != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->action), false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->fan_mode), false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->swing_mode), false); - ProtoSize::add_string_field(total_size, 1, this->custom_fan_mode, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->preset), false); - ProtoSize::add_string_field(total_size, 1, this->custom_preset, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->current_humidity != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_humidity != 0.0f, false); - ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); + ProtoSize::add_fixed_field<4>(total_size, 1, this->current_temperature != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_low != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_high != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->action)); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->fan_mode)); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->swing_mode)); + ProtoSize::add_string_field(total_size, 1, this->custom_fan_mode); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->preset)); + ProtoSize::add_string_field(total_size, 1, this->custom_preset); + ProtoSize::add_fixed_field<4>(total_size, 1, this->current_humidity != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_humidity != 0.0f); + ProtoSize::add_uint32_field(total_size, 2, this->device_id); } bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -2854,30 +2852,30 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(24, this->device_id); } void ClimateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_mode, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_low, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_low != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_high, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_high != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->unused_has_legacy_away, false); - ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away, false); - ProtoSize::add_bool_field(total_size, 1, this->has_fan_mode, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->fan_mode), false); - ProtoSize::add_bool_field(total_size, 1, this->has_swing_mode, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->swing_mode), false); - ProtoSize::add_bool_field(total_size, 2, this->has_custom_fan_mode, false); - ProtoSize::add_string_field(total_size, 2, this->custom_fan_mode, false); - ProtoSize::add_bool_field(total_size, 2, this->has_preset, false); - ProtoSize::add_enum_field(total_size, 2, static_cast(this->preset), false); - ProtoSize::add_bool_field(total_size, 2, this->has_custom_preset, false); - ProtoSize::add_string_field(total_size, 2, this->custom_preset, false); - ProtoSize::add_bool_field(total_size, 2, this->has_target_humidity, false); - ProtoSize::add_fixed_field<4>(total_size, 2, this->target_humidity != 0.0f, false); - ProtoSize::add_uint32_field(total_size, 2, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_mode); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); + ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_low); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_low != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_high); + ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_high != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->unused_has_legacy_away); + ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away); + ProtoSize::add_bool_field(total_size, 1, this->has_fan_mode); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->fan_mode)); + ProtoSize::add_bool_field(total_size, 1, this->has_swing_mode); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->swing_mode)); + ProtoSize::add_bool_field(total_size, 2, this->has_custom_fan_mode); + ProtoSize::add_string_field(total_size, 2, this->custom_fan_mode); + ProtoSize::add_bool_field(total_size, 2, this->has_preset); + ProtoSize::add_enum_field(total_size, 2, static_cast(this->preset)); + ProtoSize::add_bool_field(total_size, 2, this->has_custom_preset); + ProtoSize::add_string_field(total_size, 2, this->custom_preset); + ProtoSize::add_bool_field(total_size, 2, this->has_target_humidity); + ProtoSize::add_fixed_field<4>(total_size, 2, this->target_humidity != 0.0f); + ProtoSize::add_uint32_field(total_size, 2, this->device_id); } #endif #ifdef USE_NUMBER @@ -2972,20 +2970,20 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(14, this->device_id); } void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->min_value != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->max_value != 0.0f, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->step != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_fixed_field<4>(total_size, 1, this->min_value != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->max_value != 0.0f); + ProtoSize::add_fixed_field<4>(total_size, 1, this->step != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3022,10 +3020,10 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void NumberStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool NumberCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3057,9 +3055,9 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void NumberCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_SELECT @@ -3131,19 +3129,19 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(9, this->device_id); } void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); if (!this->options.empty()) { for (const auto &it : this->options) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SelectStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3186,10 +3184,10 @@ void SelectStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void SelectStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SelectCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3227,9 +3225,9 @@ void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void SelectCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_SIREN @@ -3311,21 +3309,21 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(11, this->device_id); } void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); if (!this->tones.empty()) { for (const auto &it : this->tones) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_bool_field(total_size, 1, this->supports_duration, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_volume, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_bool_field(total_size, 1, this->supports_duration); + ProtoSize::add_bool_field(total_size, 1, this->supports_volume); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SirenStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3357,9 +3355,9 @@ void SirenStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void SirenStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool SirenCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3432,16 +3430,16 @@ void SirenCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void SirenCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_state, false); - ProtoSize::add_bool_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->has_tone, false); - ProtoSize::add_string_field(total_size, 1, this->tone, false); - ProtoSize::add_bool_field(total_size, 1, this->has_duration, false); - ProtoSize::add_uint32_field(total_size, 1, this->duration, false); - ProtoSize::add_bool_field(total_size, 1, this->has_volume, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_state); + ProtoSize::add_bool_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->has_tone); + ProtoSize::add_string_field(total_size, 1, this->tone); + ProtoSize::add_bool_field(total_size, 1, this->has_duration); + ProtoSize::add_uint32_field(total_size, 1, this->duration); + ProtoSize::add_bool_field(total_size, 1, this->has_volume); + ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_LOCK @@ -3526,18 +3524,18 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(12, this->device_id); } void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_open, false); - ProtoSize::add_bool_field(total_size, 1, this->requires_code, false); - ProtoSize::add_string_field(total_size, 1, this->code_format, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_bool_field(total_size, 1, this->assumed_state); + ProtoSize::add_bool_field(total_size, 1, this->supports_open); + ProtoSize::add_bool_field(total_size, 1, this->requires_code); + ProtoSize::add_string_field(total_size, 1, this->code_format); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3569,9 +3567,9 @@ void LockStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void LockStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->state), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->state)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3619,11 +3617,11 @@ void LockCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->device_id); } void LockCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); - ProtoSize::add_bool_field(total_size, 1, this->has_code, false); - ProtoSize::add_string_field(total_size, 1, this->code, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); + ProtoSize::add_bool_field(total_size, 1, this->has_code); + ProtoSize::add_string_field(total_size, 1, this->code); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_BUTTON @@ -3693,15 +3691,15 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(9, this->device_id); } void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool ButtonCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3728,8 +3726,8 @@ void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->device_id); } void ButtonCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_MEDIA_PLAYER @@ -3773,11 +3771,11 @@ void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->sample_bytes); } void MediaPlayerSupportedFormat::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->format, false); - ProtoSize::add_uint32_field(total_size, 1, this->sample_rate, false); - ProtoSize::add_uint32_field(total_size, 1, this->num_channels, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->purpose), false); - ProtoSize::add_uint32_field(total_size, 1, this->sample_bytes, false); + ProtoSize::add_string_field(total_size, 1, this->format); + ProtoSize::add_uint32_field(total_size, 1, this->sample_rate); + ProtoSize::add_uint32_field(total_size, 1, this->num_channels); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->purpose)); + ProtoSize::add_uint32_field(total_size, 1, this->sample_bytes); } bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3852,16 +3850,16 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_bool_field(total_size, 1, this->supports_pause, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_bool_field(total_size, 1, this->supports_pause); ProtoSize::add_repeated_message(total_size, 1, this->supported_formats); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool MediaPlayerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3903,11 +3901,11 @@ void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->device_id); } void MediaPlayerStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->state), false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->muted, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->state)); + ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->muted); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -3980,16 +3978,16 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void MediaPlayerCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_command, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); - ProtoSize::add_bool_field(total_size, 1, this->has_volume, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->has_media_url, false); - ProtoSize::add_string_field(total_size, 1, this->media_url, false); - ProtoSize::add_bool_field(total_size, 1, this->has_announcement, false); - ProtoSize::add_bool_field(total_size, 1, this->announcement, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_command); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); + ProtoSize::add_bool_field(total_size, 1, this->has_volume); + ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->has_media_url); + ProtoSize::add_string_field(total_size, 1, this->media_url); + ProtoSize::add_bool_field(total_size, 1, this->has_announcement); + ProtoSize::add_bool_field(total_size, 1, this->announcement); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_BLUETOOTH_PROXY @@ -4007,7 +4005,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) buffer.encode_uint32(1, this->flags); } void SubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->flags, false); + ProtoSize::add_uint32_field(total_size, 1, this->flags); } bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4041,13 +4039,13 @@ void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothServiceData::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->uuid, false); + ProtoSize::add_string_field(total_size, 1, this->uuid); if (!this->legacy_data.empty()) { for (const auto &it : this->legacy_data) { - ProtoSize::add_uint32_field(total_size, 1, it, true); + ProtoSize::add_uint32_field_repeated(total_size, 1, it); } } - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4105,17 +4103,17 @@ void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(7, this->address_type); } void BluetoothLEAdvertisementResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_sint32_field(total_size, 1, this->rssi, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_sint32_field(total_size, 1, this->rssi); if (!this->service_uuids.empty()) { for (const auto &it : this->service_uuids) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } ProtoSize::add_repeated_message(total_size, 1, this->service_data); ProtoSize::add_repeated_message(total_size, 1, this->manufacturer_data); - ProtoSize::add_uint32_field(total_size, 1, this->address_type, false); + ProtoSize::add_uint32_field(total_size, 1, this->address_type); } bool BluetoothLERawAdvertisement::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4152,10 +4150,10 @@ void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(4, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothLERawAdvertisement::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_sint32_field(total_size, 1, this->rssi, false); - ProtoSize::add_uint32_field(total_size, 1, this->address_type, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_sint32_field(total_size, 1, this->rssi); + ProtoSize::add_uint32_field(total_size, 1, this->address_type); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -4204,10 +4202,10 @@ void BluetoothDeviceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->address_type); } void BluetoothDeviceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->request_type), false); - ProtoSize::add_bool_field(total_size, 1, this->has_address_type, false); - ProtoSize::add_uint32_field(total_size, 1, this->address_type, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->request_type)); + ProtoSize::add_bool_field(total_size, 1, this->has_address_type); + ProtoSize::add_uint32_field(total_size, 1, this->address_type); } bool BluetoothDeviceConnectionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4238,10 +4236,10 @@ void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(4, this->error); } void BluetoothDeviceConnectionResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_bool_field(total_size, 1, this->connected, false); - ProtoSize::add_uint32_field(total_size, 1, this->mtu, false); - ProtoSize::add_int32_field(total_size, 1, this->error, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_bool_field(total_size, 1, this->connected); + ProtoSize::add_uint32_field(total_size, 1, this->mtu); + ProtoSize::add_int32_field(total_size, 1, this->error); } bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4255,7 +4253,7 @@ bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarI } void BluetoothGATTGetServicesRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); } void BluetoothGATTGetServicesRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); } bool BluetoothGATTDescriptor::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4280,10 +4278,10 @@ void BluetoothGATTDescriptor::encode(ProtoWriteBuffer buffer) const { void BluetoothGATTDescriptor::calculate_size(uint32_t &total_size) const { if (!this->uuid.empty()) { for (const auto &it : this->uuid) { - ProtoSize::add_uint64_field(total_size, 1, it, true); + ProtoSize::add_uint64_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint32_field(total_size, 1, this->handle); } bool BluetoothGATTCharacteristic::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4326,11 +4324,11 @@ void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const { void BluetoothGATTCharacteristic::calculate_size(uint32_t &total_size) const { if (!this->uuid.empty()) { for (const auto &it : this->uuid) { - ProtoSize::add_uint64_field(total_size, 1, it, true); + ProtoSize::add_uint64_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_uint32_field(total_size, 1, this->properties, false); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_uint32_field(total_size, 1, this->properties); ProtoSize::add_repeated_message(total_size, 1, this->descriptors); } bool BluetoothGATTService::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4369,10 +4367,10 @@ void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const { void BluetoothGATTService::calculate_size(uint32_t &total_size) const { if (!this->uuid.empty()) { for (const auto &it : this->uuid) { - ProtoSize::add_uint64_field(total_size, 1, it, true); + ProtoSize::add_uint64_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint32_field(total_size, 1, this->handle); ProtoSize::add_repeated_message(total_size, 1, this->characteristics); } bool BluetoothGATTGetServicesResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4402,7 +4400,7 @@ void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const { } } void BluetoothGATTGetServicesResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); ProtoSize::add_repeated_message(total_size, 1, this->services); } bool BluetoothGATTGetServicesDoneResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4419,7 +4417,7 @@ void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer buffer) const buffer.encode_uint64(1, this->address); } void BluetoothGATTGetServicesDoneResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); } bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4440,8 +4438,8 @@ void BluetoothGATTReadRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->handle); } void BluetoothGATTReadRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); } bool BluetoothGATTReadResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4473,9 +4471,9 @@ void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTReadResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothGATTWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4512,10 +4510,10 @@ void BluetoothGATTWriteRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(4, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTWriteRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_bool_field(total_size, 1, this->response, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_bool_field(total_size, 1, this->response); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothGATTReadDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4536,8 +4534,8 @@ void BluetoothGATTReadDescriptorRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->handle); } void BluetoothGATTReadDescriptorRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); } bool BluetoothGATTWriteDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4569,9 +4567,9 @@ void BluetoothGATTWriteDescriptorRequest::encode(ProtoWriteBuffer buffer) const buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTWriteDescriptorRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4597,9 +4595,9 @@ void BluetoothGATTNotifyRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(3, this->enable); } void BluetoothGATTNotifyRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_bool_field(total_size, 1, this->enable, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_bool_field(total_size, 1, this->enable); } bool BluetoothGATTNotifyDataResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4631,9 +4629,9 @@ void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); } void BluetoothGATTNotifyDataResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_string_field(total_size, 1, this->data, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_string_field(total_size, 1, this->data); } bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4661,11 +4659,11 @@ void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const { } } void BluetoothConnectionsFreeResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->free, false); - ProtoSize::add_uint32_field(total_size, 1, this->limit, false); + ProtoSize::add_uint32_field(total_size, 1, this->free); + ProtoSize::add_uint32_field(total_size, 1, this->limit); if (!this->allocated.empty()) { for (const auto &it : this->allocated) { - ProtoSize::add_uint64_field(total_size, 1, it, true); + ProtoSize::add_uint64_field_repeated(total_size, 1, it); } } } @@ -4693,9 +4691,9 @@ void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(3, this->error); } void BluetoothGATTErrorResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); - ProtoSize::add_int32_field(total_size, 1, this->error, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); + ProtoSize::add_int32_field(total_size, 1, this->error); } bool BluetoothGATTWriteResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4716,8 +4714,8 @@ void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->handle); } void BluetoothGATTWriteResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); } bool BluetoothGATTNotifyResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4738,8 +4736,8 @@ void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->handle); } void BluetoothGATTNotifyResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_uint32_field(total_size, 1, this->handle, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_uint32_field(total_size, 1, this->handle); } bool BluetoothDevicePairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4765,9 +4763,9 @@ void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(3, this->error); } void BluetoothDevicePairingResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_bool_field(total_size, 1, this->paired, false); - ProtoSize::add_int32_field(total_size, 1, this->error, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_bool_field(total_size, 1, this->paired); + ProtoSize::add_int32_field(total_size, 1, this->error); } bool BluetoothDeviceUnpairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4793,9 +4791,9 @@ void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(3, this->error); } void BluetoothDeviceUnpairingResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_bool_field(total_size, 1, this->success, false); - ProtoSize::add_int32_field(total_size, 1, this->error, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_bool_field(total_size, 1, this->success); + ProtoSize::add_int32_field(total_size, 1, this->error); } bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4821,9 +4819,9 @@ void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(3, this->error); } void BluetoothDeviceClearCacheResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address, false); - ProtoSize::add_bool_field(total_size, 1, this->success, false); - ProtoSize::add_int32_field(total_size, 1, this->error, false); + ProtoSize::add_uint64_field(total_size, 1, this->address); + ProtoSize::add_bool_field(total_size, 1, this->success); + ProtoSize::add_int32_field(total_size, 1, this->error); } bool BluetoothScannerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4844,8 +4842,8 @@ void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(2, this->mode); } void BluetoothScannerStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->state), false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->state)); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); } bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4861,7 +4859,7 @@ void BluetoothScannerSetModeRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_enum(1, this->mode); } void BluetoothScannerSetModeRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); } #endif #ifdef USE_VOICE_ASSISTANT @@ -4884,8 +4882,8 @@ void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->flags); } void SubscribeVoiceAssistantRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->subscribe, false); - ProtoSize::add_uint32_field(total_size, 1, this->flags, false); + ProtoSize::add_bool_field(total_size, 1, this->subscribe); + ProtoSize::add_uint32_field(total_size, 1, this->flags); } bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4917,9 +4915,9 @@ void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const { buffer.encode_float(3, this->volume_multiplier); } void VoiceAssistantAudioSettings::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->noise_suppression_level, false); - ProtoSize::add_uint32_field(total_size, 1, this->auto_gain, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume_multiplier != 0.0f, false); + ProtoSize::add_uint32_field(total_size, 1, this->noise_suppression_level); + ProtoSize::add_uint32_field(total_size, 1, this->auto_gain); + ProtoSize::add_fixed_field<4>(total_size, 1, this->volume_multiplier != 0.0f); } bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4961,11 +4959,11 @@ void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(5, this->wake_word_phrase); } void VoiceAssistantRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->start, false); - ProtoSize::add_string_field(total_size, 1, this->conversation_id, false); - ProtoSize::add_uint32_field(total_size, 1, this->flags, false); - ProtoSize::add_message_object(total_size, 1, this->audio_settings, false); - ProtoSize::add_string_field(total_size, 1, this->wake_word_phrase, false); + ProtoSize::add_bool_field(total_size, 1, this->start); + ProtoSize::add_string_field(total_size, 1, this->conversation_id); + ProtoSize::add_uint32_field(total_size, 1, this->flags); + ProtoSize::add_message_object(total_size, 1, this->audio_settings); + ProtoSize::add_string_field(total_size, 1, this->wake_word_phrase); } bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -4986,8 +4984,8 @@ void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->error); } void VoiceAssistantResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->port, false); - ProtoSize::add_bool_field(total_size, 1, this->error, false); + ProtoSize::add_uint32_field(total_size, 1, this->port); + ProtoSize::add_bool_field(total_size, 1, this->error); } bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -5008,8 +5006,8 @@ void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(2, this->value); } void VoiceAssistantEventData::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->value, false); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->value); } bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5038,7 +5036,7 @@ void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const { } } void VoiceAssistantEventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type), false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type)); ProtoSize::add_repeated_message(total_size, 1, this->data); } bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -5066,8 +5064,8 @@ void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->end); } void VoiceAssistantAudio::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->data, false); - ProtoSize::add_bool_field(total_size, 1, this->end, false); + ProtoSize::add_string_field(total_size, 1, this->data); + ProtoSize::add_bool_field(total_size, 1, this->end); } bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5114,12 +5112,12 @@ void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(6, this->is_active); } void VoiceAssistantTimerEventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type), false); - ProtoSize::add_string_field(total_size, 1, this->timer_id, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_uint32_field(total_size, 1, this->total_seconds, false); - ProtoSize::add_uint32_field(total_size, 1, this->seconds_left, false); - ProtoSize::add_bool_field(total_size, 1, this->is_active, false); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type)); + ProtoSize::add_string_field(total_size, 1, this->timer_id); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_uint32_field(total_size, 1, this->total_seconds); + ProtoSize::add_uint32_field(total_size, 1, this->seconds_left); + ProtoSize::add_bool_field(total_size, 1, this->is_active); } bool VoiceAssistantAnnounceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5156,10 +5154,10 @@ void VoiceAssistantAnnounceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(4, this->start_conversation); } void VoiceAssistantAnnounceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->media_id, false); - ProtoSize::add_string_field(total_size, 1, this->text, false); - ProtoSize::add_string_field(total_size, 1, this->preannounce_media_id, false); - ProtoSize::add_bool_field(total_size, 1, this->start_conversation, false); + ProtoSize::add_string_field(total_size, 1, this->media_id); + ProtoSize::add_string_field(total_size, 1, this->text); + ProtoSize::add_string_field(total_size, 1, this->preannounce_media_id); + ProtoSize::add_bool_field(total_size, 1, this->start_conversation); } bool VoiceAssistantAnnounceFinished::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5173,7 +5171,7 @@ bool VoiceAssistantAnnounceFinished::decode_varint(uint32_t field_id, ProtoVarIn } void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); } void VoiceAssistantAnnounceFinished::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->success, false); + ProtoSize::add_bool_field(total_size, 1, this->success); } bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -5201,11 +5199,11 @@ void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const { } } void VoiceAssistantWakeWord::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->id, false); - ProtoSize::add_string_field(total_size, 1, this->wake_word, false); + ProtoSize::add_string_field(total_size, 1, this->id); + ProtoSize::add_string_field(total_size, 1, this->wake_word); if (!this->trained_languages.empty()) { for (const auto &it : this->trained_languages) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } } @@ -5246,10 +5244,10 @@ void VoiceAssistantConfigurationResponse::calculate_size(uint32_t &total_size) c ProtoSize::add_repeated_message(total_size, 1, this->available_wake_words); if (!this->active_wake_words.empty()) { for (const auto &it : this->active_wake_words) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->max_active_wake_words, false); + ProtoSize::add_uint32_field(total_size, 1, this->max_active_wake_words); } bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { @@ -5269,7 +5267,7 @@ void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { void VoiceAssistantSetConfiguration::calculate_size(uint32_t &total_size) const { if (!this->active_wake_words.empty()) { for (const auto &it : this->active_wake_words) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } } @@ -5351,17 +5349,17 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) cons buffer.encode_uint32(11, this->device_id); } void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->supported_features, false); - ProtoSize::add_bool_field(total_size, 1, this->requires_code, false); - ProtoSize::add_bool_field(total_size, 1, this->requires_code_to_arm, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->supported_features); + ProtoSize::add_bool_field(total_size, 1, this->requires_code); + ProtoSize::add_bool_field(total_size, 1, this->requires_code_to_arm); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool AlarmControlPanelStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5393,9 +5391,9 @@ void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void AlarmControlPanelStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->state), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->state)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5438,10 +5436,10 @@ void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void AlarmControlPanelCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); - ProtoSize::add_string_field(total_size, 1, this->code, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); + ProtoSize::add_string_field(total_size, 1, this->code); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_TEXT @@ -5526,18 +5524,18 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(12, this->device_id); } void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->min_length, false); - ProtoSize::add_uint32_field(total_size, 1, this->max_length, false); - ProtoSize::add_string_field(total_size, 1, this->pattern, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->min_length); + ProtoSize::add_uint32_field(total_size, 1, this->max_length); + ProtoSize::add_string_field(total_size, 1, this->pattern); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool TextStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5580,10 +5578,10 @@ void TextStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void TextStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool TextCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5621,9 +5619,9 @@ void TextCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void TextCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->state, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->state); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_DATETIME_DATE @@ -5688,14 +5686,14 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(8, this->device_id); } void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool DateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5742,12 +5740,12 @@ void DateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(6, this->device_id); } void DateStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->year, false); - ProtoSize::add_uint32_field(total_size, 1, this->month, false); - ProtoSize::add_uint32_field(total_size, 1, this->day, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->year); + ProtoSize::add_uint32_field(total_size, 1, this->month); + ProtoSize::add_uint32_field(total_size, 1, this->day); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool DateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5789,11 +5787,11 @@ void DateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->device_id); } void DateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_uint32_field(total_size, 1, this->year, false); - ProtoSize::add_uint32_field(total_size, 1, this->month, false); - ProtoSize::add_uint32_field(total_size, 1, this->day, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_uint32_field(total_size, 1, this->year); + ProtoSize::add_uint32_field(total_size, 1, this->month); + ProtoSize::add_uint32_field(total_size, 1, this->day); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_DATETIME_TIME @@ -5858,14 +5856,14 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(8, this->device_id); } void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5912,12 +5910,12 @@ void TimeStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(6, this->device_id); } void TimeStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_uint32_field(total_size, 1, this->hour, false); - ProtoSize::add_uint32_field(total_size, 1, this->minute, false); - ProtoSize::add_uint32_field(total_size, 1, this->second, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_uint32_field(total_size, 1, this->hour); + ProtoSize::add_uint32_field(total_size, 1, this->minute); + ProtoSize::add_uint32_field(total_size, 1, this->second); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool TimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -5959,11 +5957,11 @@ void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->device_id); } void TimeCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_uint32_field(total_size, 1, this->hour, false); - ProtoSize::add_uint32_field(total_size, 1, this->minute, false); - ProtoSize::add_uint32_field(total_size, 1, this->second, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_uint32_field(total_size, 1, this->hour); + ProtoSize::add_uint32_field(total_size, 1, this->minute); + ProtoSize::add_uint32_field(total_size, 1, this->second); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_EVENT @@ -6040,20 +6038,20 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(10, this->device_id); } void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); if (!this->event_types.empty()) { for (const auto &it : this->event_types) { - ProtoSize::add_string_field(total_size, 1, it, true); + ProtoSize::add_string_field_repeated(total_size, 1, it); } } - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool EventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6091,9 +6089,9 @@ void EventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void EventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->event_type, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->event_type); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_VALVE @@ -6178,18 +6176,18 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(12, this->device_id); } void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_position, false); - ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_bool_field(total_size, 1, this->assumed_state); + ProtoSize::add_bool_field(total_size, 1, this->supports_position); + ProtoSize::add_bool_field(total_size, 1, this->supports_stop); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool ValveStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6226,10 +6224,10 @@ void ValveStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void ValveStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->current_operation), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->current_operation)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool ValveCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6271,11 +6269,11 @@ void ValveCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(5, this->device_id); } void ValveCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->has_position, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f, false); - ProtoSize::add_bool_field(total_size, 1, this->stop, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->has_position); + ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); + ProtoSize::add_bool_field(total_size, 1, this->stop); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_DATETIME_DATETIME @@ -6340,14 +6338,14 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(8, this->device_id); } void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool DateTimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6384,10 +6382,10 @@ void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(4, this->device_id); } void DateTimeStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool DateTimeCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6419,9 +6417,9 @@ void DateTimeCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void DateTimeCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif #ifdef USE_UPDATE @@ -6491,15 +6489,15 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(9, this->device_id); } void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->object_id, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_string_field(total_size, 1, this->name, false); - ProtoSize::add_string_field(total_size, 1, this->unique_id, false); - ProtoSize::add_string_field(total_size, 1, this->icon, false); - ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category), false); - ProtoSize::add_string_field(total_size, 1, this->device_class, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_string_field(total_size, 1, this->object_id); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_string_field(total_size, 1, this->name); + ProtoSize::add_string_field(total_size, 1, this->unique_id); + ProtoSize::add_string_field(total_size, 1, this->icon); + ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); + ProtoSize::add_string_field(total_size, 1, this->device_class); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool UpdateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6577,17 +6575,17 @@ void UpdateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(11, this->device_id); } void UpdateStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_bool_field(total_size, 1, this->missing_state, false); - ProtoSize::add_bool_field(total_size, 1, this->in_progress, false); - ProtoSize::add_bool_field(total_size, 1, this->has_progress, false); - ProtoSize::add_fixed_field<4>(total_size, 1, this->progress != 0.0f, false); - ProtoSize::add_string_field(total_size, 1, this->current_version, false); - ProtoSize::add_string_field(total_size, 1, this->latest_version, false); - ProtoSize::add_string_field(total_size, 1, this->title, false); - ProtoSize::add_string_field(total_size, 1, this->release_summary, false); - ProtoSize::add_string_field(total_size, 1, this->release_url, false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_bool_field(total_size, 1, this->missing_state); + ProtoSize::add_bool_field(total_size, 1, this->in_progress); + ProtoSize::add_bool_field(total_size, 1, this->has_progress); + ProtoSize::add_fixed_field<4>(total_size, 1, this->progress != 0.0f); + ProtoSize::add_string_field(total_size, 1, this->current_version); + ProtoSize::add_string_field(total_size, 1, this->latest_version); + ProtoSize::add_string_field(total_size, 1, this->title); + ProtoSize::add_string_field(total_size, 1, this->release_summary); + ProtoSize::add_string_field(total_size, 1, this->release_url); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } bool UpdateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { @@ -6619,9 +6617,9 @@ void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(3, this->device_id); } void UpdateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0, false); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command), false); - ProtoSize::add_uint32_field(total_size, 1, this->device_id, false); + ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); + ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); + ProtoSize::add_uint32_field(total_size, 1, this->device_id); } #endif diff --git a/esphome/components/api/api_pb2_size.h b/esphome/components/api/api_pb2_size.h index f371be13a5..dfa1452fff 100644 --- a/esphome/components/api/api_pb2_size.h +++ b/esphome/components/api/api_pb2_size.h @@ -141,9 +141,9 @@ class ProtoSize { /** * @brief Calculates and adds the size of an int32 field to the total message size */ - static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -157,13 +157,26 @@ class ProtoSize { } } + /** + * @brief Calculates and adds the size of an int32 field to the total message size (repeated field version) + */ + static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Always calculate size for repeated fields + if (value < 0) { + // Negative values are encoded as 10-byte varints in protobuf + total_size += field_id_size + 10; + } else { + // For non-negative values, use the standard varint size + total_size += field_id_size + varint(static_cast(value)); + } + } + /** * @brief Calculates and adds the size of a uint32 field to the total message size */ - static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value, - bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -171,12 +184,20 @@ class ProtoSize { total_size += field_id_size + varint(value); } + /** + * @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version) + */ + static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + /** * @brief Calculates and adds the size of a boolean field to the total message size */ - static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value, bool force = false) { - // Skip calculation if value is false and not forced - if (!value && !force) { + static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) { + // Skip calculation if value is false + if (!value) { return; // No need to update total_size } @@ -184,6 +205,15 @@ class ProtoSize { total_size += field_id_size + 1; } + /** + * @brief Calculates and adds the size of a boolean field to the total message size (repeated field version) + */ + static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) { + // Always calculate size for repeated fields + // Boolean fields always use 1 byte + total_size += field_id_size + 1; + } + /** * @brief Calculates and adds the size of a fixed field to the total message size * @@ -193,10 +223,9 @@ class ProtoSize { * @param is_nonzero Whether the value is non-zero */ template - static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero, - bool force = false) { - // Skip calculation if value is zero and not forced - if (!is_nonzero && !force) { + static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) { + // Skip calculation if value is zero + if (!is_nonzero) { return; // No need to update total_size } @@ -209,9 +238,9 @@ class ProtoSize { * * Enum fields are encoded as uint32 varints. */ - static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value, bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -219,14 +248,25 @@ class ProtoSize { total_size += field_id_size + varint(value); } + /** + * @brief Calculates and adds the size of an enum field to the total message size (repeated field version) + * + * Enum fields are encoded as uint32 varints. + */ + static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Always calculate size for repeated fields + // Enums are encoded as uint32 + total_size += field_id_size + varint(value); + } + /** * @brief Calculates and adds the size of a sint32 field to the total message size * * Sint32 fields use ZigZag encoding, which is more efficient for negative values. */ - static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -235,12 +275,24 @@ class ProtoSize { total_size += field_id_size + varint(zigzag); } + /** + * @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version) + * + * Sint32 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Always calculate size for repeated fields + // ZigZag encoding for sint32: (n << 1) ^ (n >> 31) + uint32_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 31)); + total_size += field_id_size + varint(zigzag); + } + /** * @brief Calculates and adds the size of an int64 field to the total message size */ - static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -248,13 +300,20 @@ class ProtoSize { total_size += field_id_size + varint(value); } + /** + * @brief Calculates and adds the size of an int64 field to the total message size (repeated field version) + */ + static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + /** * @brief Calculates and adds the size of a uint64 field to the total message size */ - static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value, - bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -262,14 +321,22 @@ class ProtoSize { total_size += field_id_size + varint(value); } + /** + * @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version) + */ + static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + /** * @brief Calculates and adds the size of a sint64 field to the total message size * * Sint64 fields use ZigZag encoding, which is more efficient for negative values. */ - static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) { - // Skip calculation if value is zero and not forced - if (value == 0 && !force) { + static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Skip calculation if value is zero + if (value == 0) { return; // No need to update total_size } @@ -278,13 +345,24 @@ class ProtoSize { total_size += field_id_size + varint(zigzag); } + /** + * @brief Calculates and adds the size of a sint64 field to the total message size (repeated field version) + * + * Sint64 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Always calculate size for repeated fields + // ZigZag encoding for sint64: (n << 1) ^ (n >> 63) + uint64_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 63)); + total_size += field_id_size + varint(zigzag); + } + /** * @brief Calculates and adds the size of a string/bytes field to the total message size */ - static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str, - bool force = false) { - // Skip calculation if string is empty and not forced - if (str.empty() && !force) { + static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { + // Skip calculation if string is empty + if (str.empty()) { return; // No need to update total_size } @@ -293,18 +371,26 @@ class ProtoSize { total_size += field_id_size + varint(str_size) + str_size; } + /** + * @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version) + */ + static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { + // Always calculate size for repeated fields + const uint32_t str_size = static_cast(str.size()); + total_size += field_id_size + varint(str_size) + str_size; + } + /** * @brief Calculates and adds the size of a nested message field to the total message size * * This helper function directly updates the total_size reference if the nested size - * is greater than zero or force is true. + * is greater than zero. * * @param nested_size The pre-calculated size of the nested message */ - static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size, - bool force = false) { - // Skip calculation if nested message is empty and not forced - if (nested_size == 0 && !force) { + static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { + // Skip calculation if nested message is empty + if (nested_size == 0) { return; // No need to update total_size } @@ -313,6 +399,17 @@ class ProtoSize { total_size += field_id_size + varint(nested_size) + nested_size; } + /** + * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) + * + * @param nested_size The pre-calculated size of the nested message + */ + static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { + // Always calculate size for repeated fields + // Field ID + length varint + nested message content + total_size += field_id_size + varint(nested_size) + nested_size; + } + /** * @brief Calculates and adds the size of a nested message field to the total message size * @@ -322,13 +419,26 @@ class ProtoSize { * * @param message The nested message object */ - static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message, - bool force = false) { + static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) { uint32_t nested_size = 0; message.calculate_size(nested_size); // Use the base implementation with the calculated nested_size - add_message_field(total_size, field_id_size, nested_size, force); + add_message_field(total_size, field_id_size, nested_size); + } + + /** + * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) + * + * @param message The nested message object + */ + static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size, + const ProtoMessage &message) { + uint32_t nested_size = 0; + message.calculate_size(nested_size); + + // Use the base implementation with the calculated nested_size + add_message_field_repeated(total_size, field_id_size, nested_size); } /** @@ -348,9 +458,9 @@ class ProtoSize { return; } - // For repeated fields, always use force=true + // Use the repeated field version for all messages for (const auto &message : messages) { - add_message_object(total_size, field_id_size, message, true); + add_message_object_repeated(total_size, field_id_size, message); } } }; diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index c663af0a5f..65c51535c4 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -249,6 +249,42 @@ class TypeInfo(ABC): return 4 # 28 bits return 5 # 32 bits (maximum for uint32_t) + def _get_simple_size_calculation( + self, name: str, force: bool, base_method: str, value_expr: str = None + ) -> str: + """Helper for simple size calculations. + + Args: + name: Field name + force: Whether this is for a repeated field + base_method: Base method name (e.g., "add_int32_field") + value_expr: Optional value expression (defaults to name) + """ + field_id_size = self.calculate_field_id_size() + method = f"{base_method}_repeated" if force else base_method + value = value_expr if value_expr else name + return f"ProtoSize::{method}(total_size, {field_id_size}, {value});" + + def _get_fixed_size_calculation( + self, name: str, force: bool, num_bytes: int, zero_check: str + ) -> str: + """Helper for fixed-size field calculations. + + Args: + name: Field name + force: Whether this is for a repeated field + num_bytes: Number of bytes (4 or 8) + zero_check: Expression to check for zero value (e.g., "!= 0.0f") + """ + field_id_size = self.calculate_field_id_size() + # Fixed-size repeated fields are handled differently in RepeatedTypeInfo + # so we should never get force=True here + assert not force, ( + "Fixed-size repeated fields should be handled by RepeatedTypeInfo" + ) + method = f"add_fixed_field<{num_bytes}>" + return f"ProtoSize::{method}(total_size, {field_id_size}, {name} {zero_check});" + @abstractmethod def get_size_calculation(self, name: str, force: bool = False) -> str: """Calculate the size needed for encoding this field. @@ -258,6 +294,14 @@ class TypeInfo(ABC): force: Whether to force encoding the field even if it has a default value """ + def get_fixed_size_bytes(self) -> int | None: + """Get the number of bytes for fixed-size fields (float, double, fixed32, etc). + + Returns: + The number of bytes (4 or 8) for fixed-size fields, None for variable-size fields. + """ + return None + @abstractmethod def get_estimated_size(self) -> int: """Get estimated size in bytes for this field with typical values. @@ -295,9 +339,10 @@ class DoubleType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0.0, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 8, "!= 0.0") + + def get_fixed_size_bytes(self) -> int: + return 8 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes for double @@ -317,9 +362,10 @@ class FloatType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0.0f, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 4, "!= 0.0f") + + def get_fixed_size_bytes(self) -> int: + return 4 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 4 # field ID + 4 bytes for float @@ -339,9 +385,7 @@ class Int64Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_int64_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_int64_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -361,9 +405,7 @@ class UInt64Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_uint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_uint64_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -383,9 +425,7 @@ class Int32Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_int32_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_int32_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -405,9 +445,10 @@ class Fixed64Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 8, "!= 0") + + def get_fixed_size_bytes(self) -> int: + return 8 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed @@ -427,9 +468,10 @@ class Fixed32Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 4, "!= 0") + + def get_fixed_size_bytes(self) -> int: + return 4 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed @@ -448,9 +490,7 @@ class BoolType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_bool_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_bool_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 1 # field ID + 1 byte @@ -471,9 +511,7 @@ class StringType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_string_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical string @@ -509,9 +547,7 @@ class MessageType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_message_object(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_message_object") def get_estimated_size(self) -> int: return ( @@ -538,9 +574,7 @@ class BytesType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_string_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_string_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes typical bytes @@ -560,9 +594,7 @@ class UInt32Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_uint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_uint32_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -590,9 +622,9 @@ class EnumType(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_enum_field(total_size, {field_id_size}, static_cast({name}), {force_str(force)});" - return o + return self._get_simple_size_calculation( + name, force, "add_enum_field", f"static_cast({name})" + ) def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 1 # field ID + 1 byte typical enum @@ -612,9 +644,10 @@ class SFixed32Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<4>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 4, "!= 0") + + def get_fixed_size_bytes(self) -> int: + return 4 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 4 # field ID + 4 bytes fixed @@ -634,9 +667,10 @@ class SFixed64Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_fixed_field<8>(total_size, {field_id_size}, {name} != 0, {force_str(force)});" - return o + return self._get_fixed_size_calculation(name, force, 8, "!= 0") + + def get_fixed_size_bytes(self) -> int: + return 8 def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 8 # field ID + 8 bytes fixed @@ -656,9 +690,7 @@ class SInt32Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_sint32_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_sint32_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -678,9 +710,7 @@ class SInt64Type(TypeInfo): return o def get_size_calculation(self, name: str, force: bool = False) -> str: - field_id_size = self.calculate_field_id_size() - o = f"ProtoSize::add_sint64_field(total_size, {field_id_size}, {name}, {force_str(force)});" - return o + return self._get_simple_size_calculation(name, force, "add_sint64_field") def get_estimated_size(self) -> int: return self.calculate_field_id_size() + 3 # field ID + 3 bytes typical varint @@ -795,11 +825,23 @@ class RepeatedTypeInfo(TypeInfo): field_id_size = self._ti.calculate_field_id_size() o = f"ProtoSize::add_repeated_message(total_size, {field_id_size}, {name});" return o + # For other repeated types, use the underlying type's size calculation with force=True o = f"if (!{name}.empty()) {{\n" - o += f" for (const auto {'' if self._ti_is_bool else '&'}it : {name}) {{\n" - o += f" {self._ti.get_size_calculation('it', True)}\n" - o += " }\n" + + # Check if this is a fixed-size type by seeing if it has a fixed byte count + num_bytes = self._ti.get_fixed_size_bytes() + if num_bytes is not None: + # Fixed types have constant size per element, so we can multiply + field_id_size = self._ti.calculate_field_id_size() + # Pre-calculate the total bytes per element + bytes_per_element = field_id_size + num_bytes + o += f" total_size += {name}.size() * {bytes_per_element};\n" + else: + # Other types need the actual value + o += f" for (const auto {'' if self._ti_is_bool else '&'}it : {name}) {{\n" + o += f" {self._ti.get_size_calculation('it', True)}\n" + o += " }\n" o += "}" return o @@ -1712,7 +1754,6 @@ static const char *const TAG = "api.service"; exec_clang_format(root / "api_pb2_service.cpp") exec_clang_format(root / "api_pb2.h") exec_clang_format(root / "api_pb2.cpp") - exec_clang_format(root / "api_pb2_dump.h") exec_clang_format(root / "api_pb2_dump.cpp") except ImportError: pass From 35d88fc0d6353b450e34298878955cf39846055e Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 11 Jul 2025 22:05:06 -0500 Subject: [PATCH 13/90] [ld2410] Remove redundant ``delay()`` calls, minor optimizations (#9453) --- esphome/components/ld2410/ld2410.cpp | 40 +++++++++------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/esphome/components/ld2410/ld2410.cpp b/esphome/components/ld2410/ld2410.cpp index 375d1088e8..8f1fb0ee9d 100644 --- a/esphome/components/ld2410/ld2410.cpp +++ b/esphome/components/ld2410/ld2410.cpp @@ -178,13 +178,8 @@ static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01}; static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; } -static bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) { - for (uint8_t i = 0; i < HEADER_FOOTER_SIZE; i++) { - if (header_footer[i] != buffer[i]) { - return false; // Mismatch in header/footer - } - } - return true; // Valid header/footer +static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) { + return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0; } void LD2410Component::dump_config() { @@ -300,14 +295,12 @@ void LD2410Component::send_command_(uint8_t command, const uint8_t *command_valu if (command_value != nullptr) { len += command_value_len; } - uint8_t len_cmd[] = {lowbyte(len), highbyte(len), command, 0x00}; + // 2 length bytes (low, high) + 2 command bytes (low, high) + uint8_t len_cmd[] = {len, 0x00, command, 0x00}; this->write_array(len_cmd, sizeof(len_cmd)); - // command value bytes if (command_value != nullptr) { - for (uint8_t i = 0; i < command_value_len; i++) { - this->write_byte(command_value[i]); - } + this->write_array(command_value, command_value_len); } // frame footer bytes this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)); @@ -401,7 +394,7 @@ void LD2410Component::handle_periodic_data_() { /* Moving distance range: 18th byte Still distance range: 19th byte - Moving enery: 20~28th bytes + Moving energy: 20~28th bytes */ for (std::vector::size_type i = 0; i != this->gate_move_sensors_.size(); i++) { sensor::Sensor *s = this->gate_move_sensors_[i]; @@ -480,7 +473,7 @@ bool LD2410Component::handle_ack_data_() { ESP_LOGE(TAG, "Invalid status"); return true; } - if (ld2410::two_byte_to_int(this->buffer_data_[8], this->buffer_data_[9]) != 0x00) { + if (this->buffer_data_[8] || this->buffer_data_[9]) { ESP_LOGW(TAG, "Invalid command: %02X, %02X", this->buffer_data_[8], this->buffer_data_[9]); return true; } @@ -534,8 +527,8 @@ bool LD2410Component::handle_ack_data_() { const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_); const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_); ESP_LOGV(TAG, - "Light function is: %s\n" - "Light threshold is: %u\n" + "Light function: %s\n" + "Light threshold: %u\n" "Out pin level: %s", light_function_str, this->light_threshold_, out_pin_level_str); #ifdef USE_SELECT @@ -600,7 +593,7 @@ bool LD2410Component::handle_ack_data_() { break; case CMD_QUERY: { // Query parameters response - if (this->buffer_data_[10] != 0xAA) + if (this->buffer_data_[10] != HEADER) return true; // value head=0xAA #ifdef USE_NUMBER /* @@ -656,17 +649,11 @@ void LD2410Component::readline_(int readch) { if (this->buffer_pos_ < 4) { return; // Not enough data to process yet } - if (this->buffer_data_[this->buffer_pos_ - 4] == DATA_FRAME_FOOTER[0] && - this->buffer_data_[this->buffer_pos_ - 3] == DATA_FRAME_FOOTER[1] && - this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[2] && - this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[3]) { + if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) { ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str()); this->handle_periodic_data_(); this->buffer_pos_ = 0; // Reset position index for next message - } else if (this->buffer_data_[this->buffer_pos_ - 4] == CMD_FRAME_FOOTER[0] && - this->buffer_data_[this->buffer_pos_ - 3] == CMD_FRAME_FOOTER[1] && - this->buffer_data_[this->buffer_pos_ - 2] == CMD_FRAME_FOOTER[2] && - this->buffer_data_[this->buffer_pos_ - 1] == CMD_FRAME_FOOTER[3]) { + } else if (ld2410::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) { ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str()); if (this->handle_ack_data_()) { this->buffer_pos_ = 0; // Reset position index for next message @@ -772,7 +759,6 @@ void LD2410Component::set_max_distances_timeout() { 0x00}; this->set_config_mode_(true); this->send_command_(CMD_MAXDIST_DURATION, value, sizeof(value)); - delay(50); // NOLINT this->query_parameters_(); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_config_mode_(false); @@ -802,7 +788,6 @@ void LD2410Component::set_gate_threshold(uint8_t gate) { 0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00, 0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00}; this->send_command_(CMD_GATE_SENS, value, sizeof(value)); - delay(50); // NOLINT this->query_parameters_(); this->set_config_mode_(false); } @@ -833,7 +818,6 @@ void LD2410Component::set_light_out_control() { this->set_config_mode_(true); uint8_t value[4] = {this->light_function_, this->light_threshold_, this->out_pin_level_, 0x00}; this->send_command_(CMD_SET_LIGHT_CONTROL, value, sizeof(value)); - delay(50); // NOLINT this->query_light_control_(); this->set_timeout(200, [this]() { this->restart_and_read_all_info(); }); this->set_config_mode_(false); From 3c864b2bcae8bd06176b38b7806d79581e3a47bd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 17:56:08 -1000 Subject: [PATCH 14/90] Reduce API flash usage by eliminating unnecessary template instantiations (#9452) --- esphome/components/api/api_pb2.cpp | 360 +++++++++++++++------------- esphome/components/api/proto.h | 55 +++-- script/api_protobuf/api_protobuf.py | 42 +++- 3 files changed, 259 insertions(+), 198 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 0c110b8c8b..062ff54eb8 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -257,15 +257,17 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v return true; } case 20: { - this->devices.push_back(value.as_message()); + this->devices.emplace_back(); + value.decode_to_message(this->devices.back()); return true; } case 21: { - this->areas.push_back(value.as_message()); + this->areas.emplace_back(); + value.decode_to_message(this->areas.back()); return true; } case 22: { - this->area = value.as_message(); + value.decode_to_message(this->area); return true; } default: @@ -293,12 +295,12 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(18, this->bluetooth_mac_address); buffer.encode_bool(19, this->api_encryption_supported); for (auto &it : this->devices) { - buffer.encode_message(20, it, true); + buffer.encode_message(20, it, true); } for (auto &it : this->areas) { - buffer.encode_message(21, it, true); + buffer.encode_message(21, it, true); } - buffer.encode_message(22, this->area); + buffer.encode_message(22, this->area); } void DeviceInfoResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->uses_password); @@ -336,7 +338,7 @@ bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVar return true; } case 9: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 10: { @@ -392,7 +394,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(7, this->disabled_by_default); buffer.encode_string(8, this->icon); - buffer.encode_enum(9, this->entity_category); + buffer.encode_uint32(9, static_cast(this->entity_category)); buffer.encode_uint32(10, this->device_id); } void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) const { @@ -468,7 +470,7 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 11: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 12: { @@ -530,7 +532,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(8, this->device_class); buffer.encode_bool(9, this->disabled_by_default); buffer.encode_string(10, this->icon); - buffer.encode_enum(11, this->entity_category); + buffer.encode_uint32(11, static_cast(this->entity_category)); buffer.encode_bool(12, this->supports_stop); buffer.encode_uint32(13, this->device_id); } @@ -552,11 +554,11 @@ void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const { bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->legacy_state = value.as_enum(); + this->legacy_state = static_cast(value.as_uint32()); return true; } case 5: { - this->current_operation = value.as_enum(); + this->current_operation = static_cast(value.as_uint32()); return true; } case 6: { @@ -587,10 +589,10 @@ bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { } void CoverStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->legacy_state); + buffer.encode_uint32(2, static_cast(this->legacy_state)); buffer.encode_float(3, this->position); buffer.encode_float(4, this->tilt); - buffer.encode_enum(5, this->current_operation); + buffer.encode_uint32(5, static_cast(this->current_operation)); buffer.encode_uint32(6, this->device_id); } void CoverStateResponse::calculate_size(uint32_t &total_size) const { @@ -608,7 +610,7 @@ bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 3: { - this->legacy_command = value.as_enum(); + this->legacy_command = static_cast(value.as_uint32()); return true; } case 4: { @@ -652,7 +654,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->has_legacy_command); - buffer.encode_enum(3, this->legacy_command); + buffer.encode_uint32(3, static_cast(this->legacy_command)); buffer.encode_bool(4, this->has_position); buffer.encode_float(5, this->position); buffer.encode_bool(6, this->has_tilt); @@ -696,7 +698,7 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value return true; } case 11: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 13: { @@ -754,7 +756,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(8, this->supported_speed_count); buffer.encode_bool(9, this->disabled_by_default); buffer.encode_string(10, this->icon); - buffer.encode_enum(11, this->entity_category); + buffer.encode_uint32(11, static_cast(this->entity_category)); for (auto &it : this->supported_preset_modes) { buffer.encode_string(12, it, true); } @@ -790,11 +792,11 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 4: { - this->speed = value.as_enum(); + this->speed = static_cast(value.as_uint32()); return true; } case 5: { - this->direction = value.as_enum(); + this->direction = static_cast(value.as_uint32()); return true; } case 6: { @@ -833,8 +835,8 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); buffer.encode_bool(3, this->oscillating); - buffer.encode_enum(4, this->speed); - buffer.encode_enum(5, this->direction); + buffer.encode_uint32(4, static_cast(this->speed)); + buffer.encode_uint32(5, static_cast(this->direction)); buffer.encode_int32(6, this->speed_level); buffer.encode_string(7, this->preset_mode); buffer.encode_uint32(8, this->device_id); @@ -864,7 +866,7 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 5: { - this->speed = value.as_enum(); + this->speed = static_cast(value.as_uint32()); return true; } case 6: { @@ -880,7 +882,7 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 9: { - this->direction = value.as_enum(); + this->direction = static_cast(value.as_uint32()); return true; } case 10: { @@ -928,11 +930,11 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(2, this->has_state); buffer.encode_bool(3, this->state); buffer.encode_bool(4, this->has_speed); - buffer.encode_enum(5, this->speed); + buffer.encode_uint32(5, static_cast(this->speed)); buffer.encode_bool(6, this->has_oscillating); buffer.encode_bool(7, this->oscillating); buffer.encode_bool(8, this->has_direction); - buffer.encode_enum(9, this->direction); + buffer.encode_uint32(9, static_cast(this->direction)); buffer.encode_bool(10, this->has_speed_level); buffer.encode_int32(11, this->speed_level); buffer.encode_bool(12, this->has_preset_mode); @@ -960,7 +962,7 @@ void FanCommandRequest::calculate_size(uint32_t &total_size) const { bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 12: { - this->supported_color_modes.push_back(value.as_enum()); + this->supported_color_modes.push_back(static_cast(value.as_uint32())); return true; } case 5: { @@ -984,7 +986,7 @@ bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 15: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 16: { @@ -1045,7 +1047,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(3, this->name); buffer.encode_string(4, this->unique_id); for (auto &it : this->supported_color_modes) { - buffer.encode_enum(12, it, true); + buffer.encode_uint32(12, static_cast(it), true); } buffer.encode_bool(5, this->legacy_supports_brightness); buffer.encode_bool(6, this->legacy_supports_rgb); @@ -1058,7 +1060,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { } buffer.encode_bool(13, this->disabled_by_default); buffer.encode_string(14, this->icon); - buffer.encode_enum(15, this->entity_category); + buffer.encode_uint32(15, static_cast(this->entity_category)); buffer.encode_uint32(16, this->device_id); } void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const { @@ -1094,7 +1096,7 @@ bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 11: { - this->color_mode = value.as_enum(); + this->color_mode = static_cast(value.as_uint32()); return true; } case 14: { @@ -1165,7 +1167,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); buffer.encode_float(3, this->brightness); - buffer.encode_enum(11, this->color_mode); + buffer.encode_uint32(11, static_cast(this->color_mode)); buffer.encode_float(10, this->color_brightness); buffer.encode_float(4, this->red); buffer.encode_float(5, this->green); @@ -1212,7 +1214,7 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 23: { - this->color_mode = value.as_enum(); + this->color_mode = static_cast(value.as_uint32()); return true; } case 20: { @@ -1330,7 +1332,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(4, this->has_brightness); buffer.encode_float(5, this->brightness); buffer.encode_bool(22, this->has_color_mode); - buffer.encode_enum(23, this->color_mode); + buffer.encode_uint32(23, static_cast(this->color_mode)); buffer.encode_bool(20, this->has_color_brightness); buffer.encode_float(21, this->color_brightness); buffer.encode_bool(6, this->has_rgb); @@ -1396,11 +1398,11 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 10: { - this->state_class = value.as_enum(); + this->state_class = static_cast(value.as_uint32()); return true; } case 11: { - this->legacy_last_reset_type = value.as_enum(); + this->legacy_last_reset_type = static_cast(value.as_uint32()); return true; } case 12: { @@ -1408,7 +1410,7 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 13: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 14: { @@ -1469,10 +1471,10 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_int32(7, this->accuracy_decimals); buffer.encode_bool(8, this->force_update); buffer.encode_string(9, this->device_class); - buffer.encode_enum(10, this->state_class); - buffer.encode_enum(11, this->legacy_last_reset_type); + buffer.encode_uint32(10, static_cast(this->state_class)); + buffer.encode_uint32(11, static_cast(this->legacy_last_reset_type)); buffer.encode_bool(12, this->disabled_by_default); - buffer.encode_enum(13, this->entity_category); + buffer.encode_uint32(13, static_cast(this->entity_category)); buffer.encode_uint32(14, this->device_id); } void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const { @@ -1544,7 +1546,7 @@ bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 8: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 10: { @@ -1599,7 +1601,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->assumed_state); buffer.encode_bool(7, this->disabled_by_default); - buffer.encode_enum(8, this->entity_category); + buffer.encode_uint32(8, static_cast(this->entity_category)); buffer.encode_string(9, this->device_class); buffer.encode_uint32(10, this->device_id); } @@ -1692,7 +1694,7 @@ bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarIn return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 9: { @@ -1746,7 +1748,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class); buffer.encode_uint32(9, this->device_id); } @@ -1811,7 +1813,7 @@ void TextSensorStateResponse::calculate_size(uint32_t &total_size) const { bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->level = value.as_enum(); + this->level = static_cast(value.as_uint32()); return true; } case 2: { @@ -1823,7 +1825,7 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { } } void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->level); + buffer.encode_uint32(1, static_cast(this->level)); buffer.encode_bool(2, this->dump_config); } void SubscribeLogsRequest::calculate_size(uint32_t &total_size) const { @@ -1833,7 +1835,7 @@ void SubscribeLogsRequest::calculate_size(uint32_t &total_size) const { bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->level = value.as_enum(); + this->level = static_cast(value.as_uint32()); return true; } case 4: { @@ -1855,7 +1857,7 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite } } void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->level); + buffer.encode_uint32(1, static_cast(this->level)); buffer.encode_bytes(3, reinterpret_cast(this->message.data()), this->message.size()); buffer.encode_bool(4, this->send_failed); } @@ -1935,15 +1937,18 @@ bool HomeassistantServiceResponse::decode_length(uint32_t field_id, ProtoLengthD return true; } case 2: { - this->data.push_back(value.as_message()); + this->data.emplace_back(); + value.decode_to_message(this->data.back()); return true; } case 3: { - this->data_template.push_back(value.as_message()); + this->data_template.emplace_back(); + value.decode_to_message(this->data_template.back()); return true; } case 4: { - this->variables.push_back(value.as_message()); + this->variables.emplace_back(); + value.decode_to_message(this->variables.back()); return true; } default: @@ -1953,13 +1958,13 @@ bool HomeassistantServiceResponse::decode_length(uint32_t field_id, ProtoLengthD void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->service); for (auto &it : this->data) { - buffer.encode_message(2, it, true); + buffer.encode_message(2, it, true); } for (auto &it : this->data_template) { - buffer.encode_message(3, it, true); + buffer.encode_message(3, it, true); } for (auto &it : this->variables) { - buffer.encode_message(4, it, true); + buffer.encode_message(4, it, true); } buffer.encode_bool(5, this->is_event); } @@ -2049,7 +2054,7 @@ void GetTimeResponse::calculate_size(uint32_t &total_size) const { bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->type = value.as_enum(); + this->type = static_cast(value.as_uint32()); return true; } default: @@ -2068,7 +2073,7 @@ bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthD } void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->name); - buffer.encode_enum(2, this->type); + buffer.encode_uint32(2, static_cast(this->type)); } void ListEntitiesServicesArgument::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->name); @@ -2081,7 +2086,8 @@ bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthD return true; } case 3: { - this->args.push_back(value.as_message()); + this->args.emplace_back(); + value.decode_to_message(this->args.back()); return true; } default: @@ -2102,7 +2108,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->name); buffer.encode_fixed32(2, this->key); for (auto &it : this->args) { - buffer.encode_message(3, it, true); + buffer.encode_message(3, it, true); } } void ListEntitiesServicesResponse::calculate_size(uint32_t &total_size) const { @@ -2211,7 +2217,8 @@ void ExecuteServiceArgument::calculate_size(uint32_t &total_size) const { bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: { - this->args.push_back(value.as_message()); + this->args.emplace_back(); + value.decode_to_message(this->args.back()); return true; } default: @@ -2231,7 +2238,7 @@ bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); for (auto &it : this->args) { - buffer.encode_message(2, it, true); + buffer.encode_message(2, it, true); } } void ExecuteServiceRequest::calculate_size(uint32_t &total_size) const { @@ -2246,7 +2253,7 @@ bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -2296,7 +2303,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_bool(5, this->disabled_by_default); buffer.encode_string(6, this->icon); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->device_id); } void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const { @@ -2390,7 +2397,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v return true; } case 7: { - this->supported_modes.push_back(value.as_enum()); + this->supported_modes.push_back(static_cast(value.as_uint32())); return true; } case 11: { @@ -2402,15 +2409,15 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v return true; } case 13: { - this->supported_fan_modes.push_back(value.as_enum()); + this->supported_fan_modes.push_back(static_cast(value.as_uint32())); return true; } case 14: { - this->supported_swing_modes.push_back(value.as_enum()); + this->supported_swing_modes.push_back(static_cast(value.as_uint32())); return true; } case 16: { - this->supported_presets.push_back(value.as_enum()); + this->supported_presets.push_back(static_cast(value.as_uint32())); return true; } case 18: { @@ -2418,7 +2425,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v return true; } case 20: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 22: { @@ -2509,7 +2516,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(5, this->supports_current_temperature); buffer.encode_bool(6, this->supports_two_point_target_temperature); for (auto &it : this->supported_modes) { - buffer.encode_enum(7, it, true); + buffer.encode_uint32(7, static_cast(it), true); } buffer.encode_float(8, this->visual_min_temperature); buffer.encode_float(9, this->visual_max_temperature); @@ -2517,23 +2524,23 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(11, this->legacy_supports_away); buffer.encode_bool(12, this->supports_action); for (auto &it : this->supported_fan_modes) { - buffer.encode_enum(13, it, true); + buffer.encode_uint32(13, static_cast(it), true); } for (auto &it : this->supported_swing_modes) { - buffer.encode_enum(14, it, true); + buffer.encode_uint32(14, static_cast(it), true); } for (auto &it : this->supported_custom_fan_modes) { buffer.encode_string(15, it, true); } for (auto &it : this->supported_presets) { - buffer.encode_enum(16, it, true); + buffer.encode_uint32(16, static_cast(it), true); } for (auto &it : this->supported_custom_presets) { buffer.encode_string(17, it, true); } buffer.encode_bool(18, this->disabled_by_default); buffer.encode_string(19, this->icon); - buffer.encode_enum(20, this->entity_category); + buffer.encode_uint32(20, static_cast(this->entity_category)); buffer.encode_float(21, this->visual_current_temperature_step); buffer.encode_bool(22, this->supports_current_humidity); buffer.encode_bool(23, this->supports_target_humidity); @@ -2596,7 +2603,7 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } case 7: { @@ -2604,19 +2611,19 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { return true; } case 8: { - this->action = value.as_enum(); + this->action = static_cast(value.as_uint32()); return true; } case 9: { - this->fan_mode = value.as_enum(); + this->fan_mode = static_cast(value.as_uint32()); return true; } case 10: { - this->swing_mode = value.as_enum(); + this->swing_mode = static_cast(value.as_uint32()); return true; } case 12: { - this->preset = value.as_enum(); + this->preset = static_cast(value.as_uint32()); return true; } case 16: { @@ -2677,17 +2684,17 @@ bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { } void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->mode); + buffer.encode_uint32(2, static_cast(this->mode)); buffer.encode_float(3, this->current_temperature); buffer.encode_float(4, this->target_temperature); buffer.encode_float(5, this->target_temperature_low); buffer.encode_float(6, this->target_temperature_high); buffer.encode_bool(7, this->unused_legacy_away); - buffer.encode_enum(8, this->action); - buffer.encode_enum(9, this->fan_mode); - buffer.encode_enum(10, this->swing_mode); + buffer.encode_uint32(8, static_cast(this->action)); + buffer.encode_uint32(9, static_cast(this->fan_mode)); + buffer.encode_uint32(10, static_cast(this->swing_mode)); buffer.encode_string(11, this->custom_fan_mode); - buffer.encode_enum(12, this->preset); + buffer.encode_uint32(12, static_cast(this->preset)); buffer.encode_string(13, this->custom_preset); buffer.encode_float(14, this->current_humidity); buffer.encode_float(15, this->target_humidity); @@ -2718,7 +2725,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 3: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } case 4: { @@ -2746,7 +2753,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 13: { - this->fan_mode = value.as_enum(); + this->fan_mode = static_cast(value.as_uint32()); return true; } case 14: { @@ -2754,7 +2761,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 15: { - this->swing_mode = value.as_enum(); + this->swing_mode = static_cast(value.as_uint32()); return true; } case 16: { @@ -2766,7 +2773,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 19: { - this->preset = value.as_enum(); + this->preset = static_cast(value.as_uint32()); return true; } case 20: { @@ -2828,7 +2835,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->has_mode); - buffer.encode_enum(3, this->mode); + buffer.encode_uint32(3, static_cast(this->mode)); buffer.encode_bool(4, this->has_target_temperature); buffer.encode_float(5, this->target_temperature); buffer.encode_bool(6, this->has_target_temperature_low); @@ -2838,13 +2845,13 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(10, this->unused_has_legacy_away); buffer.encode_bool(11, this->unused_legacy_away); buffer.encode_bool(12, this->has_fan_mode); - buffer.encode_enum(13, this->fan_mode); + buffer.encode_uint32(13, static_cast(this->fan_mode)); buffer.encode_bool(14, this->has_swing_mode); - buffer.encode_enum(15, this->swing_mode); + buffer.encode_uint32(15, static_cast(this->swing_mode)); buffer.encode_bool(16, this->has_custom_fan_mode); buffer.encode_string(17, this->custom_fan_mode); buffer.encode_bool(18, this->has_preset); - buffer.encode_enum(19, this->preset); + buffer.encode_uint32(19, static_cast(this->preset)); buffer.encode_bool(20, this->has_custom_preset); buffer.encode_string(21, this->custom_preset); buffer.encode_bool(22, this->has_target_humidity); @@ -2886,11 +2893,11 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 10: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 12: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } case 14: { @@ -2963,9 +2970,9 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_float(7, this->max_value); buffer.encode_float(8, this->step); buffer.encode_bool(9, this->disabled_by_default); - buffer.encode_enum(10, this->entity_category); + buffer.encode_uint32(10, static_cast(this->entity_category)); buffer.encode_string(11, this->unit_of_measurement); - buffer.encode_enum(12, this->mode); + buffer.encode_uint32(12, static_cast(this->mode)); buffer.encode_string(13, this->device_class); buffer.encode_uint32(14, this->device_id); } @@ -3068,7 +3075,7 @@ bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 8: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 9: { @@ -3125,7 +3132,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(6, it, true); } buffer.encode_bool(7, this->disabled_by_default); - buffer.encode_enum(8, this->entity_category); + buffer.encode_uint32(8, static_cast(this->entity_category)); buffer.encode_uint32(9, this->device_id); } void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const { @@ -3246,7 +3253,7 @@ bool ListEntitiesSirenResponse::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 10: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 11: { @@ -3305,7 +3312,7 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const { } buffer.encode_bool(8, this->supports_duration); buffer.encode_bool(9, this->supports_volume); - buffer.encode_enum(10, this->entity_category); + buffer.encode_uint32(10, static_cast(this->entity_category)); buffer.encode_uint32(11, this->device_id); } void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const { @@ -3450,7 +3457,7 @@ bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt valu return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -3516,7 +3523,7 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_bool(8, this->assumed_state); buffer.encode_bool(9, this->supports_open); buffer.encode_bool(10, this->requires_code); @@ -3540,7 +3547,7 @@ void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const { bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->state = value.as_enum(); + this->state = static_cast(value.as_uint32()); return true; } case 3: { @@ -3563,7 +3570,7 @@ bool LockStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { } void LockStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->state); + buffer.encode_uint32(2, static_cast(this->state)); buffer.encode_uint32(3, this->device_id); } void LockStateResponse::calculate_size(uint32_t &total_size) const { @@ -3574,7 +3581,7 @@ void LockStateResponse::calculate_size(uint32_t &total_size) const { bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->command = value.as_enum(); + this->command = static_cast(value.as_uint32()); return true; } case 3: { @@ -3611,7 +3618,7 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { } void LockCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->command); + buffer.encode_uint32(2, static_cast(this->command)); buffer.encode_bool(3, this->has_code); buffer.encode_string(4, this->code); buffer.encode_uint32(5, this->device_id); @@ -3632,7 +3639,7 @@ bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 9: { @@ -3686,7 +3693,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class); buffer.encode_uint32(9, this->device_id); } @@ -3742,7 +3749,7 @@ bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 4: { - this->purpose = value.as_enum(); + this->purpose = static_cast(value.as_uint32()); return true; } case 5: { @@ -3767,7 +3774,7 @@ void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->format); buffer.encode_uint32(2, this->sample_rate); buffer.encode_uint32(3, this->num_channels); - buffer.encode_enum(4, this->purpose); + buffer.encode_uint32(4, static_cast(this->purpose)); buffer.encode_uint32(5, this->sample_bytes); } void MediaPlayerSupportedFormat::calculate_size(uint32_t &total_size) const { @@ -3784,7 +3791,7 @@ bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarI return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -3818,7 +3825,8 @@ bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLeng return true; } case 9: { - this->supported_formats.push_back(value.as_message()); + this->supported_formats.emplace_back(); + value.decode_to_message(this->supported_formats.back()); return true; } default: @@ -3842,10 +3850,10 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_bool(8, this->supports_pause); for (auto &it : this->supported_formats) { - buffer.encode_message(9, it, true); + buffer.encode_message(9, it, true); } buffer.encode_uint32(10, this->device_id); } @@ -3864,7 +3872,7 @@ void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const bool MediaPlayerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->state = value.as_enum(); + this->state = static_cast(value.as_uint32()); return true; } case 4: { @@ -3895,7 +3903,7 @@ bool MediaPlayerStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) } void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->state); + buffer.encode_uint32(2, static_cast(this->state)); buffer.encode_float(3, this->volume); buffer.encode_bool(4, this->muted); buffer.encode_uint32(5, this->device_id); @@ -3914,7 +3922,7 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 3: { - this->command = value.as_enum(); + this->command = static_cast(value.as_uint32()); return true; } case 4: { @@ -3968,7 +3976,7 @@ bool MediaPlayerCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->has_command); - buffer.encode_enum(3, this->command); + buffer.encode_uint32(3, static_cast(this->command)); buffer.encode_bool(4, this->has_volume); buffer.encode_float(5, this->volume); buffer.encode_bool(6, this->has_media_url); @@ -4076,11 +4084,13 @@ bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLen return true; } case 5: { - this->service_data.push_back(value.as_message()); + this->service_data.emplace_back(); + value.decode_to_message(this->service_data.back()); return true; } case 6: { - this->manufacturer_data.push_back(value.as_message()); + this->manufacturer_data.emplace_back(); + value.decode_to_message(this->manufacturer_data.back()); return true; } default: @@ -4095,10 +4105,10 @@ void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, it, true); } for (auto &it : this->service_data) { - buffer.encode_message(5, it, true); + buffer.encode_message(5, it, true); } for (auto &it : this->manufacturer_data) { - buffer.encode_message(6, it, true); + buffer.encode_message(6, it, true); } buffer.encode_uint32(7, this->address_type); } @@ -4158,7 +4168,8 @@ void BluetoothLERawAdvertisement::calculate_size(uint32_t &total_size) const { bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { - this->advertisements.push_back(value.as_message()); + this->advertisements.emplace_back(); + value.decode_to_message(this->advertisements.back()); return true; } default: @@ -4167,7 +4178,7 @@ bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, Prot } void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->advertisements) { - buffer.encode_message(1, it, true); + buffer.encode_message(1, it, true); } } void BluetoothLERawAdvertisementsResponse::calculate_size(uint32_t &total_size) const { @@ -4180,7 +4191,7 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return true; } case 2: { - this->request_type = value.as_enum(); + this->request_type = static_cast(value.as_uint32()); return true; } case 3: { @@ -4197,7 +4208,7 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) } void BluetoothDeviceRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); - buffer.encode_enum(2, this->request_type); + buffer.encode_uint32(2, static_cast(this->request_type)); buffer.encode_bool(3, this->has_address_type); buffer.encode_uint32(4, this->address_type); } @@ -4304,7 +4315,8 @@ bool BluetoothGATTCharacteristic::decode_varint(uint32_t field_id, ProtoVarInt v bool BluetoothGATTCharacteristic::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 4: { - this->descriptors.push_back(value.as_message()); + this->descriptors.emplace_back(); + value.decode_to_message(this->descriptors.back()); return true; } default: @@ -4318,7 +4330,7 @@ void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->handle); buffer.encode_uint32(3, this->properties); for (auto &it : this->descriptors) { - buffer.encode_message(4, it, true); + buffer.encode_message(4, it, true); } } void BluetoothGATTCharacteristic::calculate_size(uint32_t &total_size) const { @@ -4348,7 +4360,8 @@ bool BluetoothGATTService::decode_varint(uint32_t field_id, ProtoVarInt value) { bool BluetoothGATTService::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 3: { - this->characteristics.push_back(value.as_message()); + this->characteristics.emplace_back(); + value.decode_to_message(this->characteristics.back()); return true; } default: @@ -4361,7 +4374,7 @@ void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const { } buffer.encode_uint32(2, this->handle); for (auto &it : this->characteristics) { - buffer.encode_message(3, it, true); + buffer.encode_message(3, it, true); } } void BluetoothGATTService::calculate_size(uint32_t &total_size) const { @@ -4386,7 +4399,8 @@ bool BluetoothGATTGetServicesResponse::decode_varint(uint32_t field_id, ProtoVar bool BluetoothGATTGetServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: { - this->services.push_back(value.as_message()); + this->services.emplace_back(); + value.decode_to_message(this->services.back()); return true; } default: @@ -4396,7 +4410,7 @@ bool BluetoothGATTGetServicesResponse::decode_length(uint32_t field_id, ProtoLen void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); for (auto &it : this->services) { - buffer.encode_message(2, it, true); + buffer.encode_message(2, it, true); } } void BluetoothGATTGetServicesResponse::calculate_size(uint32_t &total_size) const { @@ -4826,11 +4840,11 @@ void BluetoothDeviceClearCacheResponse::calculate_size(uint32_t &total_size) con bool BluetoothScannerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->state = value.as_enum(); + this->state = static_cast(value.as_uint32()); return true; } case 2: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } default: @@ -4838,8 +4852,8 @@ bool BluetoothScannerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt } } void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->state); - buffer.encode_enum(2, this->mode); + buffer.encode_uint32(1, static_cast(this->state)); + buffer.encode_uint32(2, static_cast(this->mode)); } void BluetoothScannerStateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->state)); @@ -4848,7 +4862,7 @@ void BluetoothScannerStateResponse::calculate_size(uint32_t &total_size) const { bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } default: @@ -4856,7 +4870,7 @@ bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarIn } } void BluetoothScannerSetModeRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->mode); + buffer.encode_uint32(1, static_cast(this->mode)); } void BluetoothScannerSetModeRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); @@ -4940,7 +4954,7 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite return true; } case 4: { - this->audio_settings = value.as_message(); + value.decode_to_message(this->audio_settings); return true; } case 5: { @@ -4955,7 +4969,7 @@ void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); buffer.encode_string(2, this->conversation_id); buffer.encode_uint32(3, this->flags); - buffer.encode_message(4, this->audio_settings); + buffer.encode_message(4, this->audio_settings); buffer.encode_string(5, this->wake_word_phrase); } void VoiceAssistantRequest::calculate_size(uint32_t &total_size) const { @@ -5012,7 +5026,7 @@ void VoiceAssistantEventData::calculate_size(uint32_t &total_size) const { bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->event_type = value.as_enum(); + this->event_type = static_cast(value.as_uint32()); return true; } default: @@ -5022,7 +5036,8 @@ bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt v bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 2: { - this->data.push_back(value.as_message()); + this->data.emplace_back(); + value.decode_to_message(this->data.back()); return true; } default: @@ -5030,9 +5045,9 @@ bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDe } } void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->event_type); + buffer.encode_uint32(1, static_cast(this->event_type)); for (auto &it : this->data) { - buffer.encode_message(2, it, true); + buffer.encode_message(2, it, true); } } void VoiceAssistantEventResponse::calculate_size(uint32_t &total_size) const { @@ -5070,7 +5085,7 @@ void VoiceAssistantAudio::calculate_size(uint32_t &total_size) const { bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { - this->event_type = value.as_enum(); + this->event_type = static_cast(value.as_uint32()); return true; } case 4: { @@ -5104,7 +5119,7 @@ bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLen } } void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_enum(1, this->event_type); + buffer.encode_uint32(1, static_cast(this->event_type)); buffer.encode_string(2, this->timer_id); buffer.encode_string(3, this->name); buffer.encode_uint32(4, this->total_seconds); @@ -5220,7 +5235,8 @@ bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, Proto bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { - this->available_wake_words.push_back(value.as_message()); + this->available_wake_words.emplace_back(); + value.decode_to_message(this->available_wake_words.back()); return true; } case 2: { @@ -5233,7 +5249,7 @@ bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, Proto } void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->available_wake_words) { - buffer.encode_message(1, it, true); + buffer.encode_message(1, it, true); } for (auto &it : this->active_wake_words) { buffer.encode_string(2, it, true); @@ -5280,7 +5296,7 @@ bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, Pro return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -5342,7 +5358,7 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) cons buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->supported_features); buffer.encode_bool(9, this->requires_code); buffer.encode_bool(10, this->requires_code_to_arm); @@ -5364,7 +5380,7 @@ void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size) bool AlarmControlPanelStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->state = value.as_enum(); + this->state = static_cast(value.as_uint32()); return true; } case 3: { @@ -5387,7 +5403,7 @@ bool AlarmControlPanelStateResponse::decode_32bit(uint32_t field_id, Proto32Bit } void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->state); + buffer.encode_uint32(2, static_cast(this->state)); buffer.encode_uint32(3, this->device_id); } void AlarmControlPanelStateResponse::calculate_size(uint32_t &total_size) const { @@ -5398,7 +5414,7 @@ void AlarmControlPanelStateResponse::calculate_size(uint32_t &total_size) const bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->command = value.as_enum(); + this->command = static_cast(value.as_uint32()); return true; } case 4: { @@ -5431,7 +5447,7 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit } void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->command); + buffer.encode_uint32(2, static_cast(this->command)); buffer.encode_string(3, this->code); buffer.encode_uint32(4, this->device_id); } @@ -5450,7 +5466,7 @@ bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt valu return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -5462,7 +5478,7 @@ bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt valu return true; } case 11: { - this->mode = value.as_enum(); + this->mode = static_cast(value.as_uint32()); return true; } case 12: { @@ -5516,11 +5532,11 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->min_length); buffer.encode_uint32(9, this->max_length); buffer.encode_string(10, this->pattern); - buffer.encode_enum(11, this->mode); + buffer.encode_uint32(11, static_cast(this->mode)); buffer.encode_uint32(12, this->device_id); } void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const { @@ -5632,7 +5648,7 @@ bool ListEntitiesDateResponse::decode_varint(uint32_t field_id, ProtoVarInt valu return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -5682,7 +5698,7 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->device_id); } void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const { @@ -5802,7 +5818,7 @@ bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt valu return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -5852,7 +5868,7 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->device_id); } void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const { @@ -5972,7 +5988,7 @@ bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 10: { @@ -6030,7 +6046,7 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class); for (auto &it : this->event_types) { buffer.encode_string(9, it, true); @@ -6102,7 +6118,7 @@ bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt val return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 9: { @@ -6168,7 +6184,7 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class); buffer.encode_bool(9, this->assumed_state); buffer.encode_bool(10, this->supports_position); @@ -6192,7 +6208,7 @@ void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const { bool ValveStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 3: { - this->current_operation = value.as_enum(); + this->current_operation = static_cast(value.as_uint32()); return true; } case 4: { @@ -6220,7 +6236,7 @@ bool ValveStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { void ValveStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_float(2, this->position); - buffer.encode_enum(3, this->current_operation); + buffer.encode_uint32(3, static_cast(this->current_operation)); buffer.encode_uint32(4, this->device_id); } void ValveStateResponse::calculate_size(uint32_t &total_size) const { @@ -6284,7 +6300,7 @@ bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 8: { @@ -6334,7 +6350,7 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_uint32(8, this->device_id); } void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const { @@ -6430,7 +6446,7 @@ bool ListEntitiesUpdateResponse::decode_varint(uint32_t field_id, ProtoVarInt va return true; } case 7: { - this->entity_category = value.as_enum(); + this->entity_category = static_cast(value.as_uint32()); return true; } case 9: { @@ -6484,7 +6500,7 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(4, this->unique_id); buffer.encode_string(5, this->icon); buffer.encode_bool(6, this->disabled_by_default); - buffer.encode_enum(7, this->entity_category); + buffer.encode_uint32(7, static_cast(this->entity_category)); buffer.encode_string(8, this->device_class); buffer.encode_uint32(9, this->device_id); } @@ -6590,7 +6606,7 @@ void UpdateStateResponse::calculate_size(uint32_t &total_size) const { bool UpdateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->command = value.as_enum(); + this->command = static_cast(value.as_uint32()); return true; } case 3: { @@ -6613,7 +6629,7 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { } void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_enum(2, this->command); + buffer.encode_uint32(2, static_cast(this->command)); buffer.encode_uint32(3, this->device_id); } void UpdateCommandRequest::calculate_size(uint32_t &total_size) const { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 2271ba7dbd..936e732af1 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -59,7 +59,6 @@ class ProtoVarInt { uint32_t as_uint32() const { return this->value_; } uint64_t as_uint64() const { return this->value_; } bool as_bool() const { return this->value_; } - template T as_enum() const { return static_cast(this->as_uint32()); } int32_t as_int32() const { // Not ZigZag encoded return static_cast(this->as_int64()); @@ -133,15 +132,24 @@ class ProtoVarInt { uint64_t value_; }; +// Forward declaration for decode_to_message and encode_to_writer +class ProtoMessage; + class ProtoLengthDelimited { public: explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {} std::string as_string() const { return std::string(reinterpret_cast(this->value_), this->length_); } - template C as_message() const { - auto msg = C(); - msg.decode(this->value_, this->length_); - return msg; - } + + /** + * Decode the length-delimited data into an existing ProtoMessage instance. + * + * This method allows decoding without templates, enabling use in contexts + * where the message type is not known at compile time. The ProtoMessage's + * decode() method will be called with the raw data and length. + * + * @param msg The ProtoMessage instance to decode into + */ + void decode_to_message(ProtoMessage &msg) const; protected: const uint8_t *const value_; @@ -263,9 +271,6 @@ class ProtoWriteBuffer { this->write((value >> 48) & 0xFF); this->write((value >> 56) & 0xFF); } - template void encode_enum(uint32_t field_id, T value, bool force = false) { - this->encode_uint32(field_id, static_cast(value), force); - } void encode_float(uint32_t field_id, float value, bool force = false) { if (value == 0.0f && !force) return; @@ -306,18 +311,7 @@ class ProtoWriteBuffer { } this->encode_uint64(field_id, uvalue, force); } - template void encode_message(uint32_t field_id, const C &value, bool force = false) { - this->encode_field_raw(field_id, 2); // type 2: Length-delimited message - size_t begin = this->buffer_->size(); - - value.encode(*this); - - const uint32_t nested_length = this->buffer_->size() - begin; - // add size varint - std::vector var; - ProtoVarInt(nested_length).encode(var); - this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end()); - } + void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = false); std::vector *get_buffer() const { return buffer_; } protected: @@ -345,6 +339,25 @@ class ProtoMessage { virtual bool decode_64bit(uint32_t field_id, Proto64Bit value) { return false; } }; +// Implementation of encode_message - must be after ProtoMessage is defined +inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) { + this->encode_field_raw(field_id, 2); // type 2: Length-delimited message + size_t begin = this->buffer_->size(); + + value.encode(*this); + + const uint32_t nested_length = this->buffer_->size() - begin; + // add size varint + std::vector var; + ProtoVarInt(nested_length).encode(var); + this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end()); +} + +// Implementation of decode_to_message - must be after ProtoMessage is defined +inline void ProtoLengthDelimited::decode_to_message(ProtoMessage &msg) const { + msg.decode(this->value_, this->length_); +} + template const char *proto_enum_to_string(T value); class ProtoService { diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 65c51535c4..1bb8789904 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -536,11 +536,26 @@ class MessageType(TypeInfo): @property def encode_func(self) -> str: - return f"encode_message<{self.cpp_type}>" + return "encode_message" @property def decode_length(self) -> str: - return f"value.as_message<{self.cpp_type}>()" + # Override to return None for message types because we can't use template-based + # decoding when the specific message type isn't known at compile time. + # Instead, we use the non-template decode_to_message() method which allows + # runtime polymorphism through virtual function calls. + return None + + @property + def decode_length_content(self) -> str: + # Custom decode that doesn't use templates + return dedent( + f"""\ + case {self.number}: {{ + value.decode_to_message(this->{self.field_name}); + return true; + }}""" + ) def dump(self, name: str) -> str: o = f"{name}.dump_to(out);" @@ -608,14 +623,18 @@ class EnumType(TypeInfo): @property def decode_varint(self) -> str: - return f"value.as_enum<{self.cpp_type}>()" + return f"static_cast<{self.cpp_type}>(value.as_uint32())" default_value = "" wire_type = WireType.VARINT # Uses wire type 0 @property def encode_func(self) -> str: - return f"encode_enum<{self.cpp_type}>" + return "encode_uint32" + + @property + def encode_content(self) -> str: + return f"buffer.{self.encode_func}({self.number}, static_cast(this->{self.field_name}));" def dump(self, name: str) -> str: o = f"out.append(proto_enum_to_string<{self.cpp_type}>({name}));" @@ -757,6 +776,16 @@ class RepeatedTypeInfo(TypeInfo): @property def decode_length_content(self) -> str: content = self._ti.decode_length + if content is None and isinstance(self._ti, MessageType): + # Special handling for non-template message decoding + return dedent( + f"""\ + case {self.number}: {{ + this->{self.field_name}.emplace_back(); + value.decode_to_message(this->{self.field_name}.back()); + return true; + }}""" + ) if content is None: return None return dedent( @@ -801,7 +830,10 @@ class RepeatedTypeInfo(TypeInfo): @property def encode_content(self) -> str: o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n" - o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" + if isinstance(self._ti, EnumType): + o += f" buffer.{self._ti.encode_func}({self.number}, static_cast(it), true);\n" + else: + o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" o += "}" return o From 42947bcf566b39a3b3a21d31a9b0a330dc099939 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Jul 2025 19:08:03 -1000 Subject: [PATCH 15/90] Conditionally compile API user services to save 4.3KB flash (follow-up to #9262) (#9451) --- esphome/components/api/__init__.py | 24 ++- esphome/components/api/api.proto | 4 + esphome/components/api/api_connection.cpp | 2 + esphome/components/api/api_connection.h | 2 + esphome/components/api/api_pb2.cpp | 2 + esphome/components/api/api_pb2.h | 4 + esphome/components/api/api_pb2_dump.cpp | 4 + esphome/components/api/api_pb2_service.cpp | 4 + esphome/components/api/api_pb2_service.h | 6 + esphome/components/api/api_server.cpp | 8 - esphome/components/api/api_server.h | 40 +---- esphome/components/api/custom_api_device.h | 8 + esphome/components/api/list_entities.cpp | 2 + esphome/components/api/list_entities.h | 2 + esphome/components/api/user_services.h | 2 + esphome/core/component_iterator.cpp | 6 +- esphome/core/component_iterator.h | 6 +- esphome/core/defines.h | 2 +- .../fixtures/api_custom_services.yaml | 24 +++ .../custom_api_device_component/__init__.py | 19 +++ .../custom_api_device_component.cpp | 53 +++++++ .../custom_api_device_component.h | 29 ++++ tests/integration/test_api_custom_services.py | 144 ++++++++++++++++++ 23 files changed, 345 insertions(+), 52 deletions(-) create mode 100644 tests/integration/fixtures/api_custom_services.yaml create mode 100644 tests/integration/fixtures/external_components/custom_api_device_component/__init__.py create mode 100644 tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.cpp create mode 100644 tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.h create mode 100644 tests/integration/test_api_custom_services.py diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index eb8883b025..5b302760b1 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -24,8 +24,9 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VARIABLES, ) -from esphome.core import coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority +DOMAIN = "api" DEPENDENCIES = ["network"] AUTO_LOAD = ["socket"] CODEOWNERS = ["@OttoWinter"] @@ -51,6 +52,7 @@ SERVICE_ARG_NATIVE_TYPES = { } CONF_ENCRYPTION = "encryption" CONF_BATCH_DELAY = "batch_delay" +CONF_CUSTOM_SERVICES = "custom_services" def validate_encryption_key(value): @@ -115,6 +117,7 @@ CONFIG_SCHEMA = cv.All( cv.positive_time_period_milliseconds, cv.Range(max=cv.TimePeriod(milliseconds=65535)), ), + cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean, cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( single=True ), @@ -139,8 +142,11 @@ async def to_code(config): cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY])) + # Set USE_API_SERVICES if any services are enabled + if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]: + cg.add_define("USE_API_SERVICES") + if actions := config.get(CONF_ACTIONS, []): - cg.add_define("USE_API_YAML_SERVICES") for conf in actions: template_args = [] func_args = [] @@ -317,7 +323,10 @@ async def api_connected_to_code(config, condition_id, template_arg, args): def FILTER_SOURCE_FILES() -> list[str]: - """Filter out api_pb2_dump.cpp when proto message dumping is not enabled.""" + """Filter out api_pb2_dump.cpp when proto message dumping is not enabled + and user_services.cpp when no services are defined.""" + files_to_filter = [] + # api_pb2_dump.cpp is only needed when HAS_PROTO_MESSAGE_DUMP is defined # This is a particularly large file that still needs to be opened and read # all the way to the end even when ifdef'd out @@ -325,6 +334,11 @@ def FILTER_SOURCE_FILES() -> list[str]: # HAS_PROTO_MESSAGE_DUMP is defined when ESPHOME_LOG_HAS_VERY_VERBOSE is set, # which happens when the logger level is VERY_VERBOSE if get_logger_level() != "VERY_VERBOSE": - return ["api_pb2_dump.cpp"] + files_to_filter.append("api_pb2_dump.cpp") - return [] + # user_services.cpp is only needed when services are defined + config = CORE.config.get(DOMAIN, {}) + if config and not config.get(CONF_ACTIONS) and not config[CONF_CUSTOM_SERVICES]: + files_to_filter.append("user_services.cpp") + + return files_to_filter diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index c35e603628..861b3471d7 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -807,18 +807,21 @@ enum ServiceArgType { SERVICE_ARG_TYPE_STRING_ARRAY = 7; } message ListEntitiesServicesArgument { + option (ifdef) = "USE_API_SERVICES"; string name = 1; ServiceArgType type = 2; } message ListEntitiesServicesResponse { option (id) = 41; option (source) = SOURCE_SERVER; + option (ifdef) = "USE_API_SERVICES"; string name = 1; fixed32 key = 2; repeated ListEntitiesServicesArgument args = 3; } message ExecuteServiceArgument { + option (ifdef) = "USE_API_SERVICES"; bool bool_ = 1; int32 legacy_int = 2; float float_ = 3; @@ -834,6 +837,7 @@ message ExecuteServiceRequest { option (id) = 42; option (source) = SOURCE_CLIENT; option (no_delay) = true; + option (ifdef) = "USE_API_SERVICES"; fixed32 key = 1; repeated ExecuteServiceArgument args = 2; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 3b0b4858a9..ea3268a583 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1551,6 +1551,7 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes } } } +#ifdef USE_API_SERVICES void APIConnection::execute_service(const ExecuteServiceRequest &msg) { bool found = false; for (auto *service : this->parent_->get_user_services()) { @@ -1562,6 +1563,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) { ESP_LOGV(TAG, "Could not find service"); } } +#endif #ifdef USE_API_NOISE NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) { psk_t psk{}; diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index fdc2fb3529..0051a143de 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -195,7 +195,9 @@ class APIConnection : public APIServerConnection { // TODO return {}; } +#ifdef USE_API_SERVICES void execute_service(const ExecuteServiceRequest &msg) override; +#endif #ifdef USE_API_NOISE NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override; #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 062ff54eb8..b7906654cb 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2051,6 +2051,7 @@ void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixe void GetTimeResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0); } +#ifdef USE_API_SERVICES bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -2245,6 +2246,7 @@ void ExecuteServiceRequest::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); ProtoSize::add_repeated_message(total_size, 1, this->args); } +#endif #ifdef USE_CAMERA bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 3c4e0dfb6d..7b57b2766e 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -82,6 +82,7 @@ enum LogLevel : uint32_t { LOG_LEVEL_VERBOSE = 6, LOG_LEVEL_VERY_VERBOSE = 7, }; +#ifdef USE_API_SERVICES enum ServiceArgType : uint32_t { SERVICE_ARG_TYPE_BOOL = 0, SERVICE_ARG_TYPE_INT = 1, @@ -92,6 +93,7 @@ enum ServiceArgType : uint32_t { SERVICE_ARG_TYPE_FLOAT_ARRAY = 6, SERVICE_ARG_TYPE_STRING_ARRAY = 7, }; +#endif #ifdef USE_CLIMATE enum ClimateMode : uint32_t { CLIMATE_MODE_OFF = 0, @@ -1203,6 +1205,7 @@ class GetTimeResponse : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; +#ifdef USE_API_SERVICES class ListEntitiesServicesArgument : public ProtoMessage { public: std::string name{}; @@ -1278,6 +1281,7 @@ class ExecuteServiceRequest : public ProtoMessage { bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; +#endif #ifdef USE_CAMERA class ListEntitiesCameraResponse : public InfoResponseProtoMessage { public: diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index 7991e20bc5..f6509f47cc 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -162,6 +162,7 @@ template<> const char *proto_enum_to_string(enums::LogLevel val return "UNKNOWN"; } } +#ifdef USE_API_SERVICES template<> const char *proto_enum_to_string(enums::ServiceArgType value) { switch (value) { case enums::SERVICE_ARG_TYPE_BOOL: @@ -184,6 +185,7 @@ template<> const char *proto_enum_to_string(enums::Servic return "UNKNOWN"; } } +#endif #ifdef USE_CLIMATE template<> const char *proto_enum_to_string(enums::ClimateMode value) { switch (value) { @@ -1811,6 +1813,7 @@ void GetTimeResponse::dump_to(std::string &out) const { out.append("\n"); out.append("}"); } +#ifdef USE_API_SERVICES void ListEntitiesServicesArgument::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("ListEntitiesServicesArgument {\n"); @@ -1910,6 +1913,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const { } out.append("}"); } +#endif #ifdef USE_CAMERA void ListEntitiesCameraResponse::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 92dd90053b..b96e5736a4 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -195,6 +195,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_home_assistant_state_response(msg); break; } +#ifdef USE_API_SERVICES case 42: { ExecuteServiceRequest msg; msg.decode(msg_data, msg_size); @@ -204,6 +205,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, this->on_execute_service_request(msg); break; } +#endif #ifdef USE_CAMERA case 45: { CameraImageRequest msg; @@ -660,11 +662,13 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { } } } +#ifdef USE_API_SERVICES void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { if (this->check_authenticated_()) { this->execute_service(msg); } } +#endif #ifdef USE_API_NOISE void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) { if (this->check_authenticated_()) { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 458f8ec81b..9c5dc244fe 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -69,7 +69,9 @@ class APIServerConnectionBase : public ProtoService { virtual void on_get_time_request(const GetTimeRequest &value){}; virtual void on_get_time_response(const GetTimeResponse &value){}; +#ifdef USE_API_SERVICES virtual void on_execute_service_request(const ExecuteServiceRequest &value){}; +#endif #ifdef USE_CAMERA virtual void on_camera_image_request(const CameraImageRequest &value){}; @@ -216,7 +218,9 @@ class APIServerConnection : public APIServerConnectionBase { virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; +#ifdef USE_API_SERVICES virtual void execute_service(const ExecuteServiceRequest &msg) = 0; +#endif #ifdef USE_API_NOISE virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0; #endif @@ -333,7 +337,9 @@ class APIServerConnection : public APIServerConnectionBase { void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override; void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; void on_get_time_request(const GetTimeRequest &msg) override; +#ifdef USE_API_SERVICES void on_execute_service_request(const ExecuteServiceRequest &msg) override; +#endif #ifdef USE_API_NOISE void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override; #endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 6a5d273ec1..f5be672c9a 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -24,14 +24,6 @@ static const char *const TAG = "api"; // APIServer APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -#ifndef USE_API_YAML_SERVICES -// Global empty vector to avoid guard variables (saves 8 bytes) -// This is initialized at program startup before any threads -static const std::vector empty_user_services{}; - -const std::vector &get_empty_user_services_instance() { return empty_user_services; } -#endif - APIServer::APIServer() { global_api_server = this; // Pre-allocate shared write buffer diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index f34fd55974..f41064b62b 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -12,7 +12,9 @@ #include "esphome/core/log.h" #include "list_entities.h" #include "subscribe_state.h" +#ifdef USE_API_SERVICES #include "user_services.h" +#endif #include @@ -25,11 +27,6 @@ struct SavedNoisePsk { } PACKED; // NOLINT #endif -#ifndef USE_API_YAML_SERVICES -// Forward declaration of helper function -const std::vector &get_empty_user_services_instance(); -#endif - class APIServer : public Component, public Controller { public: APIServer(); @@ -112,18 +109,9 @@ class APIServer : public Component, public Controller { void on_media_player_update(media_player::MediaPlayer *obj) override; #endif void send_homeassistant_service_call(const HomeassistantServiceResponse &call); - void register_user_service(UserServiceDescriptor *descriptor) { -#ifdef USE_API_YAML_SERVICES - // Vector is pre-allocated when services are defined in YAML - this->user_services_.push_back(descriptor); -#else - // Lazy allocate vector on first use for CustomAPIDevice - if (!this->user_services_) { - this->user_services_ = std::make_unique>(); - } - this->user_services_->push_back(descriptor); +#ifdef USE_API_SERVICES + void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #endif - } #ifdef USE_HOMEASSISTANT_TIME void request_time(); #endif @@ -152,17 +140,9 @@ class APIServer : public Component, public Controller { void get_home_assistant_state(std::string entity_id, optional attribute, std::function f); const std::vector &get_state_subs() const; - const std::vector &get_user_services() const { -#ifdef USE_API_YAML_SERVICES - return this->user_services_; -#else - if (this->user_services_) { - return *this->user_services_; - } - // Return reference to global empty instance (no guard needed) - return get_empty_user_services_instance(); +#ifdef USE_API_SERVICES + const std::vector &get_user_services() const { return this->user_services_; } #endif - } #ifdef USE_API_CLIENT_CONNECTED_TRIGGER Trigger *get_client_connected_trigger() const { return this->client_connected_trigger_; } @@ -194,14 +174,8 @@ class APIServer : public Component, public Controller { #endif std::vector shared_write_buffer_; // Shared proto write buffer for all connections std::vector state_subs_; -#ifdef USE_API_YAML_SERVICES - // When services are defined in YAML, we know at compile time that services will be registered +#ifdef USE_API_SERVICES std::vector user_services_; -#else - // Services can still be registered at runtime by CustomAPIDevice components even when not - // defined in YAML. Using unique_ptr allows lazy allocation, saving 12 bytes in the common - // case where no services (YAML or custom) are used. - std::unique_ptr> user_services_; #endif // Group smaller types together diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index 1a8e189f41..35329c4a5e 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -3,10 +3,13 @@ #include #include "api_server.h" #ifdef USE_API +#ifdef USE_API_SERVICES #include "user_services.h" +#endif namespace esphome { namespace api { +#ifdef USE_API_SERVICES template class CustomAPIDeviceService : public UserServiceBase { public: CustomAPIDeviceService(const std::string &name, const std::array &arg_names, T *obj, @@ -19,6 +22,7 @@ template class CustomAPIDeviceService : public UserS T *obj_; void (T::*callback_)(Ts...); }; +#endif // USE_API_SERVICES class CustomAPIDevice { public: @@ -46,12 +50,14 @@ class CustomAPIDevice { * @param name The name of the service to register. * @param arg_names The name of the arguments for the service, must match the arguments of the function. */ +#ifdef USE_API_SERVICES template void register_service(void (T::*callback)(Ts...), const std::string &name, const std::array &arg_names) { auto *service = new CustomAPIDeviceService(name, arg_names, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); } +#endif /** Register a custom native API service that will show up in Home Assistant. * @@ -71,10 +77,12 @@ class CustomAPIDevice { * @param callback The member function to call when the service is triggered. * @param name The name of the arguments for the service, must match the arguments of the function. */ +#ifdef USE_API_SERVICES template void register_service(void (T::*callback)(), const std::string &name) { auto *service = new CustomAPIDeviceService(name, {}, (T *) this, callback); // NOLINT global_api_server->register_user_service(service); } +#endif /** Subscribe to the state (or attribute state) of an entity from Home Assistant. * diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index 60814e359d..1fbe68117b 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -83,10 +83,12 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done( ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {} +#ifdef USE_API_SERVICES bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) { auto resp = service->encode_list_service_response(); return this->client_->send_message(resp); } +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 5e6074e008..b4cbf6c489 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -44,7 +44,9 @@ class ListEntitiesIterator : public ComponentIterator { #ifdef USE_TEXT_SENSOR bool on_text_sensor(text_sensor::TextSensor *entity) override; #endif +#ifdef USE_API_SERVICES bool on_service(UserServiceDescriptor *service) override; +#endif #ifdef USE_CAMERA bool on_camera(camera::Camera *entity) override; #endif diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 673bcf5693..93cea8133f 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -7,6 +7,7 @@ #include "esphome/core/automation.h" #include "api_pb2.h" +#ifdef USE_API_SERVICES namespace esphome { namespace api { @@ -73,3 +74,4 @@ template class UserServiceTrigger : public UserServiceBaseat_ >= api::global_api_server->get_user_services().size()) { advance_platform = true; @@ -383,7 +385,7 @@ void ComponentIterator::advance() { } bool ComponentIterator::on_end() { return true; } bool ComponentIterator::on_begin() { return true; } -#ifdef USE_API +#ifdef USE_API_SERVICES bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; } #endif #ifdef USE_CAMERA diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index eda786be7f..ea2c8004ac 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -10,7 +10,7 @@ namespace esphome { -#ifdef USE_API +#ifdef USE_API_SERVICES namespace api { class UserServiceDescriptor; } // namespace api @@ -45,7 +45,7 @@ class ComponentIterator { #ifdef USE_TEXT_SENSOR virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0; #endif -#ifdef USE_API +#ifdef USE_API_SERVICES virtual bool on_service(api::UserServiceDescriptor *service); #endif #ifdef USE_CAMERA @@ -122,7 +122,7 @@ class ComponentIterator { #ifdef USE_TEXT_SENSOR TEXT_SENSOR, #endif -#ifdef USE_API +#ifdef USE_API_SERVICES SERVICE, #endif #ifdef USE_CAMERA diff --git a/esphome/core/defines.h b/esphome/core/defines.h index d73009436b..8ed8f4b5aa 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -108,7 +108,7 @@ #define USE_API_CLIENT_DISCONNECTED_TRIGGER #define USE_API_NOISE #define USE_API_PLAINTEXT -#define USE_API_YAML_SERVICES +#define USE_API_SERVICES #define USE_MD5 #define USE_MQTT #define USE_NETWORK diff --git a/tests/integration/fixtures/api_custom_services.yaml b/tests/integration/fixtures/api_custom_services.yaml new file mode 100644 index 0000000000..41efc95b85 --- /dev/null +++ b/tests/integration/fixtures/api_custom_services.yaml @@ -0,0 +1,24 @@ +esphome: + name: api-custom-services-test +host: + +# This is required for CustomAPIDevice to work +api: + custom_services: true + # Also test that YAML services still work + actions: + - action: test_yaml_service + then: + - logger.log: "YAML service called" + +logger: + level: DEBUG + +# External component that uses CustomAPIDevice +external_components: + - source: + type: local + path: EXTERNAL_COMPONENT_PATH + components: [custom_api_device_component] + +custom_api_device_component: diff --git a/tests/integration/fixtures/external_components/custom_api_device_component/__init__.py b/tests/integration/fixtures/external_components/custom_api_device_component/__init__.py new file mode 100644 index 0000000000..127082601a --- /dev/null +++ b/tests/integration/fixtures/external_components/custom_api_device_component/__init__.py @@ -0,0 +1,19 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID + +custom_api_device_component_ns = cg.esphome_ns.namespace("custom_api_device_component") +CustomAPIDeviceComponent = custom_api_device_component_ns.class_( + "CustomAPIDeviceComponent", cg.Component +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomAPIDeviceComponent), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) diff --git a/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.cpp b/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.cpp new file mode 100644 index 0000000000..c8581b3d2f --- /dev/null +++ b/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.cpp @@ -0,0 +1,53 @@ +#include "custom_api_device_component.h" +#include "esphome/core/log.h" + +#ifdef USE_API +namespace esphome { +namespace custom_api_device_component { + +static const char *const TAG = "custom_api"; + +void CustomAPIDeviceComponent::setup() { + // Register services using CustomAPIDevice + register_service(&CustomAPIDeviceComponent::on_test_service, "custom_test_service"); + + register_service(&CustomAPIDeviceComponent::on_service_with_args, "custom_service_with_args", + {"arg_string", "arg_int", "arg_bool", "arg_float"}); + + // Test array types + register_service(&CustomAPIDeviceComponent::on_service_with_arrays, "custom_service_with_arrays", + {"bool_array", "int_array", "float_array", "string_array"}); +} + +void CustomAPIDeviceComponent::on_test_service() { ESP_LOGI(TAG, "Custom test service called!"); } + +// NOLINTNEXTLINE(performance-unnecessary-value-param) +void CustomAPIDeviceComponent::on_service_with_args(std::string arg_string, int32_t arg_int, bool arg_bool, + float arg_float) { + ESP_LOGI(TAG, "Custom service called with: %s, %d, %d, %.2f", arg_string.c_str(), arg_int, arg_bool, arg_float); +} + +void CustomAPIDeviceComponent::on_service_with_arrays(std::vector bool_array, std::vector int_array, + std::vector float_array, + std::vector string_array) { + ESP_LOGI(TAG, "Array service called with %zu bools, %zu ints, %zu floats, %zu strings", bool_array.size(), + int_array.size(), float_array.size(), string_array.size()); + + // Log first element of each array if not empty + if (!bool_array.empty()) { + ESP_LOGI(TAG, "First bool: %s", bool_array[0] ? "true" : "false"); + } + if (!int_array.empty()) { + ESP_LOGI(TAG, "First int: %d", int_array[0]); + } + if (!float_array.empty()) { + ESP_LOGI(TAG, "First float: %.2f", float_array[0]); + } + if (!string_array.empty()) { + ESP_LOGI(TAG, "First string: %s", string_array[0].c_str()); + } +} + +} // namespace custom_api_device_component +} // namespace esphome +#endif // USE_API diff --git a/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.h b/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.h new file mode 100644 index 0000000000..92960746d9 --- /dev/null +++ b/tests/integration/fixtures/external_components/custom_api_device_component/custom_api_device_component.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include "esphome/core/component.h" +#include "esphome/components/api/custom_api_device.h" + +#ifdef USE_API +namespace esphome { +namespace custom_api_device_component { + +using namespace api; + +class CustomAPIDeviceComponent : public Component, public CustomAPIDevice { + public: + void setup() override; + + void on_test_service(); + + // NOLINTNEXTLINE(performance-unnecessary-value-param) + void on_service_with_args(std::string arg_string, int32_t arg_int, bool arg_bool, float arg_float); + + void on_service_with_arrays(std::vector bool_array, std::vector int_array, + std::vector float_array, std::vector string_array); +}; + +} // namespace custom_api_device_component +} // namespace esphome +#endif // USE_API diff --git a/tests/integration/test_api_custom_services.py b/tests/integration/test_api_custom_services.py new file mode 100644 index 0000000000..2862dcc900 --- /dev/null +++ b/tests/integration/test_api_custom_services.py @@ -0,0 +1,144 @@ +"""Integration test for API custom services using CustomAPIDevice.""" + +from __future__ import annotations + +import asyncio +from pathlib import Path +import re + +from aioesphomeapi import UserService, UserServiceArgType +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_api_custom_services( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test CustomAPIDevice services work correctly with custom_services: true.""" + # Get the path to the external components directory + external_components_path = str( + Path(__file__).parent / "fixtures" / "external_components" + ) + + # Replace the placeholder in the YAML config with the actual path + yaml_config = yaml_config.replace( + "EXTERNAL_COMPONENT_PATH", external_components_path + ) + + loop = asyncio.get_running_loop() + + # Track log messages + yaml_service_future = loop.create_future() + custom_service_future = loop.create_future() + custom_args_future = loop.create_future() + custom_arrays_future = loop.create_future() + + # Patterns to match in logs + yaml_service_pattern = re.compile(r"YAML service called") + custom_service_pattern = re.compile(r"Custom test service called!") + custom_args_pattern = re.compile( + r"Custom service called with: test_string, 456, 1, 78\.90" + ) + custom_arrays_pattern = re.compile( + r"Array service called with 2 bools, 3 ints, 2 floats, 2 strings" + ) + + def check_output(line: str) -> None: + """Check log output for expected messages.""" + if not yaml_service_future.done() and yaml_service_pattern.search(line): + yaml_service_future.set_result(True) + elif not custom_service_future.done() and custom_service_pattern.search(line): + custom_service_future.set_result(True) + elif not custom_args_future.done() and custom_args_pattern.search(line): + custom_args_future.set_result(True) + elif not custom_arrays_future.done() and custom_arrays_pattern.search(line): + custom_arrays_future.set_result(True) + + # Run with log monitoring + async with run_compiled(yaml_config, line_callback=check_output): + async with api_client_connected() as client: + # Verify device info + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "api-custom-services-test" + + # List services + _, services = await client.list_entities_services() + + # Should have 4 services: 1 YAML + 3 CustomAPIDevice + assert len(services) == 4, f"Expected 4 services, found {len(services)}" + + # Find our services + yaml_service: UserService | None = None + custom_service: UserService | None = None + custom_args_service: UserService | None = None + custom_arrays_service: UserService | None = None + + for service in services: + if service.name == "test_yaml_service": + yaml_service = service + elif service.name == "custom_test_service": + custom_service = service + elif service.name == "custom_service_with_args": + custom_args_service = service + elif service.name == "custom_service_with_arrays": + custom_arrays_service = service + + assert yaml_service is not None, "test_yaml_service not found" + assert custom_service is not None, "custom_test_service not found" + assert custom_args_service is not None, "custom_service_with_args not found" + assert custom_arrays_service is not None, ( + "custom_service_with_arrays not found" + ) + + # Test YAML service + client.execute_service(yaml_service, {}) + await asyncio.wait_for(yaml_service_future, timeout=5.0) + + # Test simple CustomAPIDevice service + client.execute_service(custom_service, {}) + await asyncio.wait_for(custom_service_future, timeout=5.0) + + # Verify custom_args_service arguments + assert len(custom_args_service.args) == 4 + arg_types = {arg.name: arg.type for arg in custom_args_service.args} + assert arg_types["arg_string"] == UserServiceArgType.STRING + assert arg_types["arg_int"] == UserServiceArgType.INT + assert arg_types["arg_bool"] == UserServiceArgType.BOOL + assert arg_types["arg_float"] == UserServiceArgType.FLOAT + + # Test CustomAPIDevice service with arguments + client.execute_service( + custom_args_service, + { + "arg_string": "test_string", + "arg_int": 456, + "arg_bool": True, + "arg_float": 78.9, + }, + ) + await asyncio.wait_for(custom_args_future, timeout=5.0) + + # Verify array service arguments + assert len(custom_arrays_service.args) == 4 + array_arg_types = {arg.name: arg.type for arg in custom_arrays_service.args} + assert array_arg_types["bool_array"] == UserServiceArgType.BOOL_ARRAY + assert array_arg_types["int_array"] == UserServiceArgType.INT_ARRAY + assert array_arg_types["float_array"] == UserServiceArgType.FLOAT_ARRAY + assert array_arg_types["string_array"] == UserServiceArgType.STRING_ARRAY + + # Test CustomAPIDevice service with arrays + client.execute_service( + custom_arrays_service, + { + "bool_array": [True, False], + "int_array": [1, 2, 3], + "float_array": [1.1, 2.2], + "string_array": ["hello", "world"], + }, + ) + await asyncio.wait_for(custom_arrays_future, timeout=5.0) From 1d9f17a57c14e2790cb624de8739a66ad071f51c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 12 Jul 2025 17:00:52 +1000 Subject: [PATCH 16/90] [packet_transport] Don't run update if ping_pong not enabled. (#9434) --- esphome/components/packet_transport/packet_transport.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/packet_transport/packet_transport.cpp b/esphome/components/packet_transport/packet_transport.cpp index 6684d43ff7..b6ce24bc1b 100644 --- a/esphome/components/packet_transport/packet_transport.cpp +++ b/esphome/components/packet_transport/packet_transport.cpp @@ -314,6 +314,9 @@ void PacketTransport::send_data_(bool all) { } void PacketTransport::update() { + if (!this->ping_pong_enable_) { + return; + } auto now = millis() / 1000; if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) { this->resend_ping_key_ = this->ping_pong_enable_; From 80fbe2808872997ea432a76c88eecb1bd8b78b09 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sat, 12 Jul 2025 03:03:03 -0400 Subject: [PATCH 17/90] [sx127x, sx126x] Fix preamble_size default and validation (#9454) --- esphome/components/sx126x/__init__.py | 6 +++--- esphome/components/sx127x/__init__.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/sx126x/__init__.py b/esphome/components/sx126x/__init__.py index 492febe283..b6aeaf072c 100644 --- a/esphome/components/sx126x/__init__.py +++ b/esphome/components/sx126x/__init__.py @@ -167,8 +167,8 @@ def validate_config(config): if config[CONF_MODULATION] == "LORA": if config[CONF_BANDWIDTH] not in lora_bws: raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with LORA") - if config[CONF_PREAMBLE_SIZE] > 0 and config[CONF_PREAMBLE_SIZE] < 6: - raise cv.Invalid("Minimum preamble size is 6 with LORA") + if config[CONF_PREAMBLE_SIZE] < 6: + raise cv.Invalid("Minimum 'preamble_size' is 6 with LORA") if config[CONF_SPREADING_FACTOR] == 6 and config[CONF_PAYLOAD_LENGTH] == 0: raise cv.Invalid("Payload length must be set when spreading factor is 6") else: @@ -200,7 +200,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_PA_RAMP, default="40us"): cv.enum(RAMP), cv.Optional(CONF_PAYLOAD_LENGTH, default=0): cv.int_range(min=0, max=256), cv.Optional(CONF_PREAMBLE_DETECT, default=2): cv.int_range(min=0, max=4), - cv.Required(CONF_PREAMBLE_SIZE): cv.int_range(min=1, max=65535), + cv.Optional(CONF_PREAMBLE_SIZE, default=8): cv.int_range(min=1, max=65535), cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RX_START, default=True): cv.boolean, cv.Required(CONF_RF_SWITCH): cv.boolean, diff --git a/esphome/components/sx127x/__init__.py b/esphome/components/sx127x/__init__.py index 4d034801cc..33b556db07 100644 --- a/esphome/components/sx127x/__init__.py +++ b/esphome/components/sx127x/__init__.py @@ -164,8 +164,8 @@ def validate_config(config): raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with LORA") if CONF_DIO0_PIN not in config: raise cv.Invalid("Cannot use LoRa without dio0_pin") - if 0 < config[CONF_PREAMBLE_SIZE] < 6: - raise cv.Invalid("Minimum preamble size is 6 with LORA") + if config[CONF_PREAMBLE_SIZE] < 6: + raise cv.Invalid("Minimum 'preamble_size' is 6 with LORA") if config[CONF_SPREADING_FACTOR] == 6 and config[CONF_PAYLOAD_LENGTH] == 0: raise cv.Invalid("Payload length must be set when spreading factor is 6") else: From 8605994cc6022488a814a9cfe26471e0a4fdf406 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 12 Jul 2025 08:55:32 -1000 Subject: [PATCH 18/90] Apply existing protobuf buffer optimization to nested message encoding (~2.3x speed up) (#9458) --- esphome/components/api/api_frame_helper.cpp | 1 - esphome/components/api/api_pb2.cpp | 1 - esphome/components/api/api_pb2.h | 1 - esphome/components/api/api_pb2_size.h | 469 ------------------- esphome/components/api/proto.h | 482 +++++++++++++++++++- script/api_protobuf/api_protobuf.py | 2 - 6 files changed, 476 insertions(+), 480 deletions(-) delete mode 100644 esphome/components/api/api_pb2_size.h diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index 156fd42cb3..afd64e8981 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -5,7 +5,6 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" #include "proto.h" -#include "api_pb2_size.h" #include #include diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index b7906654cb..74bb08ce60 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1,7 +1,6 @@ // This file was automatically generated with a tool. // See script/api_protobuf/api_protobuf.py #include "api_pb2.h" -#include "api_pb2_size.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 7b57b2766e..6a95055c2b 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -5,7 +5,6 @@ #include "esphome/core/defines.h" #include "proto.h" -#include "api_pb2_size.h" namespace esphome { namespace api { diff --git a/esphome/components/api/api_pb2_size.h b/esphome/components/api/api_pb2_size.h deleted file mode 100644 index dfa1452fff..0000000000 --- a/esphome/components/api/api_pb2_size.h +++ /dev/null @@ -1,469 +0,0 @@ -#pragma once - -#include "proto.h" -#include -#include - -namespace esphome { -namespace api { - -class ProtoSize { - public: - /** - * @brief ProtoSize class for Protocol Buffer serialization size calculation - * - * This class provides static methods to calculate the exact byte counts needed - * for encoding various Protocol Buffer field types. All methods are designed to be - * efficient for the common case where many fields have default values. - * - * Implements Protocol Buffer encoding size calculation according to: - * https://protobuf.dev/programming-guides/encoding/ - * - * Key features: - * - Early-return optimization for zero/default values - * - Direct total_size updates to avoid unnecessary additions - * - Specialized handling for different field types according to protobuf spec - * - Templated helpers for repeated fields and messages - */ - - /** - * @brief Calculates the size in bytes needed to encode a uint32_t value as a varint - * - * @param value The uint32_t value to calculate size for - * @return The number of bytes needed to encode the value - */ - static inline uint32_t varint(uint32_t value) { - // Optimized varint size calculation using leading zeros - // Each 7 bits requires one byte in the varint encoding - if (value < 128) - return 1; // 7 bits, common case for small values - - // For larger values, count bytes needed based on the position of the highest bit set - if (value < 16384) { - return 2; // 14 bits - } else if (value < 2097152) { - return 3; // 21 bits - } else if (value < 268435456) { - return 4; // 28 bits - } else { - return 5; // 32 bits (maximum for uint32_t) - } - } - - /** - * @brief Calculates the size in bytes needed to encode a uint64_t value as a varint - * - * @param value The uint64_t value to calculate size for - * @return The number of bytes needed to encode the value - */ - static inline uint32_t varint(uint64_t value) { - // Handle common case of values fitting in uint32_t (vast majority of use cases) - if (value <= UINT32_MAX) { - return varint(static_cast(value)); - } - - // For larger values, determine size based on highest bit position - if (value < (1ULL << 35)) { - return 5; // 35 bits - } else if (value < (1ULL << 42)) { - return 6; // 42 bits - } else if (value < (1ULL << 49)) { - return 7; // 49 bits - } else if (value < (1ULL << 56)) { - return 8; // 56 bits - } else if (value < (1ULL << 63)) { - return 9; // 63 bits - } else { - return 10; // 64 bits (maximum for uint64_t) - } - } - - /** - * @brief Calculates the size in bytes needed to encode an int32_t value as a varint - * - * Special handling is needed for negative values, which are sign-extended to 64 bits - * in Protocol Buffers, resulting in a 10-byte varint. - * - * @param value The int32_t value to calculate size for - * @return The number of bytes needed to encode the value - */ - static inline uint32_t varint(int32_t value) { - // Negative values are sign-extended to 64 bits in protocol buffers, - // which always results in a 10-byte varint for negative int32 - if (value < 0) { - return 10; // Negative int32 is always 10 bytes long - } - // For non-negative values, use the uint32_t implementation - return varint(static_cast(value)); - } - - /** - * @brief Calculates the size in bytes needed to encode an int64_t value as a varint - * - * @param value The int64_t value to calculate size for - * @return The number of bytes needed to encode the value - */ - static inline uint32_t varint(int64_t value) { - // For int64_t, we convert to uint64_t and calculate the size - // This works because the bit pattern determines the encoding size, - // and we've handled negative int32 values as a special case above - return varint(static_cast(value)); - } - - /** - * @brief Calculates the size in bytes needed to encode a field ID and wire type - * - * @param field_id The field identifier - * @param type The wire type value (from the WireType enum in the protobuf spec) - * @return The number of bytes needed to encode the field ID and wire type - */ - static inline uint32_t field(uint32_t field_id, uint32_t type) { - uint32_t tag = (field_id << 3) | (type & 0b111); - return varint(tag); - } - - /** - * @brief Common parameters for all add_*_field methods - * - * All add_*_field methods follow these common patterns: - * - * @param total_size Reference to the total message size to update - * @param field_id_size Pre-calculated size of the field ID in bytes - * @param value The value to calculate size for (type varies) - * @param force Whether to calculate size even if the value is default/zero/empty - * - * Each method follows this implementation pattern: - * 1. Skip calculation if value is default (0, false, empty) and not forced - * 2. Calculate the size based on the field's encoding rules - * 3. Add the field_id_size + calculated value size to total_size - */ - - /** - * @brief Calculates and adds the size of an int32 field to the total message size - */ - static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - if (value < 0) { - // Negative values are encoded as 10-byte varints in protobuf - total_size += field_id_size + 10; - } else { - // For non-negative values, use the standard varint size - total_size += field_id_size + varint(static_cast(value)); - } - } - - /** - * @brief Calculates and adds the size of an int32 field to the total message size (repeated field version) - */ - static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { - // Always calculate size for repeated fields - if (value < 0) { - // Negative values are encoded as 10-byte varints in protobuf - total_size += field_id_size + 10; - } else { - // For non-negative values, use the standard varint size - total_size += field_id_size + varint(static_cast(value)); - } - } - - /** - * @brief Calculates and adds the size of a uint32 field to the total message size - */ - static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version) - */ - static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { - // Always calculate size for repeated fields - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a boolean field to the total message size - */ - static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) { - // Skip calculation if value is false - if (!value) { - return; // No need to update total_size - } - - // Boolean fields always use 1 byte when true - total_size += field_id_size + 1; - } - - /** - * @brief Calculates and adds the size of a boolean field to the total message size (repeated field version) - */ - static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) { - // Always calculate size for repeated fields - // Boolean fields always use 1 byte - total_size += field_id_size + 1; - } - - /** - * @brief Calculates and adds the size of a fixed field to the total message size - * - * Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double). - * - * @tparam NumBytes The number of bytes for this fixed field (4 or 8) - * @param is_nonzero Whether the value is non-zero - */ - template - static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) { - // Skip calculation if value is zero - if (!is_nonzero) { - return; // No need to update total_size - } - - // Fixed fields always take exactly NumBytes - total_size += field_id_size + NumBytes; - } - - /** - * @brief Calculates and adds the size of an enum field to the total message size - * - * Enum fields are encoded as uint32 varints. - */ - static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // Enums are encoded as uint32 - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of an enum field to the total message size (repeated field version) - * - * Enum fields are encoded as uint32 varints. - */ - static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { - // Always calculate size for repeated fields - // Enums are encoded as uint32 - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a sint32 field to the total message size - * - * Sint32 fields use ZigZag encoding, which is more efficient for negative values. - */ - static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // ZigZag encoding for sint32: (n << 1) ^ (n >> 31) - uint32_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 31)); - total_size += field_id_size + varint(zigzag); - } - - /** - * @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version) - * - * Sint32 fields use ZigZag encoding, which is more efficient for negative values. - */ - static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { - // Always calculate size for repeated fields - // ZigZag encoding for sint32: (n << 1) ^ (n >> 31) - uint32_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 31)); - total_size += field_id_size + varint(zigzag); - } - - /** - * @brief Calculates and adds the size of an int64 field to the total message size - */ - static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of an int64 field to the total message size (repeated field version) - */ - static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { - // Always calculate size for repeated fields - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a uint64 field to the total message size - */ - static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version) - */ - static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { - // Always calculate size for repeated fields - total_size += field_id_size + varint(value); - } - - /** - * @brief Calculates and adds the size of a sint64 field to the total message size - * - * Sint64 fields use ZigZag encoding, which is more efficient for negative values. - */ - static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { - // Skip calculation if value is zero - if (value == 0) { - return; // No need to update total_size - } - - // ZigZag encoding for sint64: (n << 1) ^ (n >> 63) - uint64_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 63)); - total_size += field_id_size + varint(zigzag); - } - - /** - * @brief Calculates and adds the size of a sint64 field to the total message size (repeated field version) - * - * Sint64 fields use ZigZag encoding, which is more efficient for negative values. - */ - static inline void add_sint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { - // Always calculate size for repeated fields - // ZigZag encoding for sint64: (n << 1) ^ (n >> 63) - uint64_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 63)); - total_size += field_id_size + varint(zigzag); - } - - /** - * @brief Calculates and adds the size of a string/bytes field to the total message size - */ - static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { - // Skip calculation if string is empty - if (str.empty()) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - const uint32_t str_size = static_cast(str.size()); - total_size += field_id_size + varint(str_size) + str_size; - } - - /** - * @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version) - */ - static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { - // Always calculate size for repeated fields - const uint32_t str_size = static_cast(str.size()); - total_size += field_id_size + varint(str_size) + str_size; - } - - /** - * @brief Calculates and adds the size of a nested message field to the total message size - * - * This helper function directly updates the total_size reference if the nested size - * is greater than zero. - * - * @param nested_size The pre-calculated size of the nested message - */ - static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { - // Skip calculation if nested message is empty - if (nested_size == 0) { - return; // No need to update total_size - } - - // Calculate and directly add to total_size - // Field ID + length varint + nested message content - total_size += field_id_size + varint(nested_size) + nested_size; - } - - /** - * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) - * - * @param nested_size The pre-calculated size of the nested message - */ - static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { - // Always calculate size for repeated fields - // Field ID + length varint + nested message content - total_size += field_id_size + varint(nested_size) + nested_size; - } - - /** - * @brief Calculates and adds the size of a nested message field to the total message size - * - * This version takes a ProtoMessage object, calculates its size internally, - * and updates the total_size reference. This eliminates the need for a temporary variable - * at the call site. - * - * @param message The nested message object - */ - static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) { - uint32_t nested_size = 0; - message.calculate_size(nested_size); - - // Use the base implementation with the calculated nested_size - add_message_field(total_size, field_id_size, nested_size); - } - - /** - * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) - * - * @param message The nested message object - */ - static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size, - const ProtoMessage &message) { - uint32_t nested_size = 0; - message.calculate_size(nested_size); - - // Use the base implementation with the calculated nested_size - add_message_field_repeated(total_size, field_id_size, nested_size); - } - - /** - * @brief Calculates and adds the sizes of all messages in a repeated field to the total message size - * - * This helper processes a vector of message objects, calculating the size for each message - * and adding it to the total size. - * - * @tparam MessageType The type of the nested messages in the vector - * @param messages Vector of message objects - */ - template - static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size, - const std::vector &messages) { - // Skip if the vector is empty - if (messages.empty()) { - return; - } - - // Use the repeated field version for all messages - for (const auto &message : messages) { - add_message_object_repeated(total_size, field_id_size, message); - } - } -}; - -} // namespace api -} // namespace esphome diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 936e732af1..a435168821 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -4,6 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include #include #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE @@ -339,18 +340,487 @@ class ProtoMessage { virtual bool decode_64bit(uint32_t field_id, Proto64Bit value) { return false; } }; +class ProtoSize { + public: + /** + * @brief ProtoSize class for Protocol Buffer serialization size calculation + * + * This class provides static methods to calculate the exact byte counts needed + * for encoding various Protocol Buffer field types. All methods are designed to be + * efficient for the common case where many fields have default values. + * + * Implements Protocol Buffer encoding size calculation according to: + * https://protobuf.dev/programming-guides/encoding/ + * + * Key features: + * - Early-return optimization for zero/default values + * - Direct total_size updates to avoid unnecessary additions + * - Specialized handling for different field types according to protobuf spec + * - Templated helpers for repeated fields and messages + */ + + /** + * @brief Calculates the size in bytes needed to encode a uint32_t value as a varint + * + * @param value The uint32_t value to calculate size for + * @return The number of bytes needed to encode the value + */ + static inline uint32_t varint(uint32_t value) { + // Optimized varint size calculation using leading zeros + // Each 7 bits requires one byte in the varint encoding + if (value < 128) + return 1; // 7 bits, common case for small values + + // For larger values, count bytes needed based on the position of the highest bit set + if (value < 16384) { + return 2; // 14 bits + } else if (value < 2097152) { + return 3; // 21 bits + } else if (value < 268435456) { + return 4; // 28 bits + } else { + return 5; // 32 bits (maximum for uint32_t) + } + } + + /** + * @brief Calculates the size in bytes needed to encode a uint64_t value as a varint + * + * @param value The uint64_t value to calculate size for + * @return The number of bytes needed to encode the value + */ + static inline uint32_t varint(uint64_t value) { + // Handle common case of values fitting in uint32_t (vast majority of use cases) + if (value <= UINT32_MAX) { + return varint(static_cast(value)); + } + + // For larger values, determine size based on highest bit position + if (value < (1ULL << 35)) { + return 5; // 35 bits + } else if (value < (1ULL << 42)) { + return 6; // 42 bits + } else if (value < (1ULL << 49)) { + return 7; // 49 bits + } else if (value < (1ULL << 56)) { + return 8; // 56 bits + } else if (value < (1ULL << 63)) { + return 9; // 63 bits + } else { + return 10; // 64 bits (maximum for uint64_t) + } + } + + /** + * @brief Calculates the size in bytes needed to encode an int32_t value as a varint + * + * Special handling is needed for negative values, which are sign-extended to 64 bits + * in Protocol Buffers, resulting in a 10-byte varint. + * + * @param value The int32_t value to calculate size for + * @return The number of bytes needed to encode the value + */ + static inline uint32_t varint(int32_t value) { + // Negative values are sign-extended to 64 bits in protocol buffers, + // which always results in a 10-byte varint for negative int32 + if (value < 0) { + return 10; // Negative int32 is always 10 bytes long + } + // For non-negative values, use the uint32_t implementation + return varint(static_cast(value)); + } + + /** + * @brief Calculates the size in bytes needed to encode an int64_t value as a varint + * + * @param value The int64_t value to calculate size for + * @return The number of bytes needed to encode the value + */ + static inline uint32_t varint(int64_t value) { + // For int64_t, we convert to uint64_t and calculate the size + // This works because the bit pattern determines the encoding size, + // and we've handled negative int32 values as a special case above + return varint(static_cast(value)); + } + + /** + * @brief Calculates the size in bytes needed to encode a field ID and wire type + * + * @param field_id The field identifier + * @param type The wire type value (from the WireType enum in the protobuf spec) + * @return The number of bytes needed to encode the field ID and wire type + */ + static inline uint32_t field(uint32_t field_id, uint32_t type) { + uint32_t tag = (field_id << 3) | (type & 0b111); + return varint(tag); + } + + /** + * @brief Common parameters for all add_*_field methods + * + * All add_*_field methods follow these common patterns: + * + * @param total_size Reference to the total message size to update + * @param field_id_size Pre-calculated size of the field ID in bytes + * @param value The value to calculate size for (type varies) + * @param force Whether to calculate size even if the value is default/zero/empty + * + * Each method follows this implementation pattern: + * 1. Skip calculation if value is default (0, false, empty) and not forced + * 2. Calculate the size based on the field's encoding rules + * 3. Add the field_id_size + calculated value size to total_size + */ + + /** + * @brief Calculates and adds the size of an int32 field to the total message size + */ + static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + if (value < 0) { + // Negative values are encoded as 10-byte varints in protobuf + total_size += field_id_size + 10; + } else { + // For non-negative values, use the standard varint size + total_size += field_id_size + varint(static_cast(value)); + } + } + + /** + * @brief Calculates and adds the size of an int32 field to the total message size (repeated field version) + */ + static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Always calculate size for repeated fields + if (value < 0) { + // Negative values are encoded as 10-byte varints in protobuf + total_size += field_id_size + 10; + } else { + // For non-negative values, use the standard varint size + total_size += field_id_size + varint(static_cast(value)); + } + } + + /** + * @brief Calculates and adds the size of a uint32 field to the total message size + */ + static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version) + */ + static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a boolean field to the total message size + */ + static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) { + // Skip calculation if value is false + if (!value) { + return; // No need to update total_size + } + + // Boolean fields always use 1 byte when true + total_size += field_id_size + 1; + } + + /** + * @brief Calculates and adds the size of a boolean field to the total message size (repeated field version) + */ + static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) { + // Always calculate size for repeated fields + // Boolean fields always use 1 byte + total_size += field_id_size + 1; + } + + /** + * @brief Calculates and adds the size of a fixed field to the total message size + * + * Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double). + * + * @tparam NumBytes The number of bytes for this fixed field (4 or 8) + * @param is_nonzero Whether the value is non-zero + */ + template + static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) { + // Skip calculation if value is zero + if (!is_nonzero) { + return; // No need to update total_size + } + + // Fixed fields always take exactly NumBytes + total_size += field_id_size + NumBytes; + } + + /** + * @brief Calculates and adds the size of an enum field to the total message size + * + * Enum fields are encoded as uint32 varints. + */ + static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // Enums are encoded as uint32 + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of an enum field to the total message size (repeated field version) + * + * Enum fields are encoded as uint32 varints. + */ + static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) { + // Always calculate size for repeated fields + // Enums are encoded as uint32 + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a sint32 field to the total message size + * + * Sint32 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // ZigZag encoding for sint32: (n << 1) ^ (n >> 31) + uint32_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 31)); + total_size += field_id_size + varint(zigzag); + } + + /** + * @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version) + * + * Sint32 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) { + // Always calculate size for repeated fields + // ZigZag encoding for sint32: (n << 1) ^ (n >> 31) + uint32_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 31)); + total_size += field_id_size + varint(zigzag); + } + + /** + * @brief Calculates and adds the size of an int64 field to the total message size + */ + static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of an int64 field to the total message size (repeated field version) + */ + static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a uint64 field to the total message size + */ + static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version) + */ + static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) { + // Always calculate size for repeated fields + total_size += field_id_size + varint(value); + } + + /** + * @brief Calculates and adds the size of a sint64 field to the total message size + * + * Sint64 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Skip calculation if value is zero + if (value == 0) { + return; // No need to update total_size + } + + // ZigZag encoding for sint64: (n << 1) ^ (n >> 63) + uint64_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 63)); + total_size += field_id_size + varint(zigzag); + } + + /** + * @brief Calculates and adds the size of a sint64 field to the total message size (repeated field version) + * + * Sint64 fields use ZigZag encoding, which is more efficient for negative values. + */ + static inline void add_sint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) { + // Always calculate size for repeated fields + // ZigZag encoding for sint64: (n << 1) ^ (n >> 63) + uint64_t zigzag = (static_cast(value) << 1) ^ (static_cast(value >> 63)); + total_size += field_id_size + varint(zigzag); + } + + /** + * @brief Calculates and adds the size of a string/bytes field to the total message size + */ + static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { + // Skip calculation if string is empty + if (str.empty()) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + const uint32_t str_size = static_cast(str.size()); + total_size += field_id_size + varint(str_size) + str_size; + } + + /** + * @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version) + */ + static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) { + // Always calculate size for repeated fields + const uint32_t str_size = static_cast(str.size()); + total_size += field_id_size + varint(str_size) + str_size; + } + + /** + * @brief Calculates and adds the size of a nested message field to the total message size + * + * This helper function directly updates the total_size reference if the nested size + * is greater than zero. + * + * @param nested_size The pre-calculated size of the nested message + */ + static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { + // Skip calculation if nested message is empty + if (nested_size == 0) { + return; // No need to update total_size + } + + // Calculate and directly add to total_size + // Field ID + length varint + nested message content + total_size += field_id_size + varint(nested_size) + nested_size; + } + + /** + * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) + * + * @param nested_size The pre-calculated size of the nested message + */ + static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) { + // Always calculate size for repeated fields + // Field ID + length varint + nested message content + total_size += field_id_size + varint(nested_size) + nested_size; + } + + /** + * @brief Calculates and adds the size of a nested message field to the total message size + * + * This version takes a ProtoMessage object, calculates its size internally, + * and updates the total_size reference. This eliminates the need for a temporary variable + * at the call site. + * + * @param message The nested message object + */ + static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) { + uint32_t nested_size = 0; + message.calculate_size(nested_size); + + // Use the base implementation with the calculated nested_size + add_message_field(total_size, field_id_size, nested_size); + } + + /** + * @brief Calculates and adds the size of a nested message field to the total message size (repeated field version) + * + * @param message The nested message object + */ + static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size, + const ProtoMessage &message) { + uint32_t nested_size = 0; + message.calculate_size(nested_size); + + // Use the base implementation with the calculated nested_size + add_message_field_repeated(total_size, field_id_size, nested_size); + } + + /** + * @brief Calculates and adds the sizes of all messages in a repeated field to the total message size + * + * This helper processes a vector of message objects, calculating the size for each message + * and adding it to the total size. + * + * @tparam MessageType The type of the nested messages in the vector + * @param messages Vector of message objects + */ + template + static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size, + const std::vector &messages) { + // Skip if the vector is empty + if (messages.empty()) { + return; + } + + // Use the repeated field version for all messages + for (const auto &message : messages) { + add_message_object_repeated(total_size, field_id_size, message); + } + } +}; + // Implementation of encode_message - must be after ProtoMessage is defined inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) { this->encode_field_raw(field_id, 2); // type 2: Length-delimited message - size_t begin = this->buffer_->size(); + // Calculate the message size first + uint32_t msg_length_bytes = 0; + value.calculate_size(msg_length_bytes); + + // Calculate how many bytes the length varint needs + uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes); + + // Reserve exact space for the length varint + size_t begin = this->buffer_->size(); + this->buffer_->resize(this->buffer_->size() + varint_length_bytes); + + // Write the length varint directly + ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes); + + // Now encode the message content - it will append to the buffer value.encode(*this); - const uint32_t nested_length = this->buffer_->size() - begin; - // add size varint - std::vector var; - ProtoVarInt(nested_length).encode(var); - this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end()); + // Verify that the encoded size matches what we calculated + assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes); } // Implementation of decode_to_message - must be after ProtoMessage is defined diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 1bb8789904..2482521398 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1451,7 +1451,6 @@ def main() -> None: #include "esphome/core/defines.h" #include "proto.h" -#include "api_pb2_size.h" namespace esphome { namespace api { @@ -1461,7 +1460,6 @@ namespace api { cpp = FILE_HEADER cpp += """\ #include "api_pb2.h" - #include "api_pb2_size.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" From 36dd203e74689d000d9d7a297f2fae91075bdfa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 12 Jul 2025 10:07:58 -1000 Subject: [PATCH 19/90] Bump aioesphomeapi from 34.2.0 to 34.2.1 (#9460) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d056f22e28..ea264a8ac4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==4.9.0 click==8.1.7 esphome-dashboard==20250514.0 -aioesphomeapi==34.2.0 +aioesphomeapi==34.2.1 zeroconf==0.147.0 puremagic==1.30 ruamel.yaml==0.18.14 # dashboard_import From bc7cfeb9cddc78193c11a35808cc52ad363b9fdc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 12 Jul 2025 12:58:57 -1000 Subject: [PATCH 20/90] Only generate protobuf encode/decode methods for the message direction they're used (#9461) --- esphome/components/api/api_pb2.cpp | 3432 --------------------------- esphome/components/api/api_pb2.h | 240 -- script/api_protobuf/api_protobuf.py | 42 +- 3 files changed, 25 insertions(+), 3689 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 74bb08ce60..4c0e20e0f0 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -31,44 +31,6 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) return false; } } -void HelloRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_string(1, this->client_info); - buffer.encode_uint32(2, this->api_version_major); - buffer.encode_uint32(3, this->api_version_minor); -} -void HelloRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->client_info); - ProtoSize::add_uint32_field(total_size, 1, this->api_version_major); - ProtoSize::add_uint32_field(total_size, 1, this->api_version_minor); -} -bool HelloResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->api_version_major = value.as_uint32(); - return true; - } - case 2: { - this->api_version_minor = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool HelloResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 3: { - this->server_info = value.as_string(); - return true; - } - case 4: { - this->name = value.as_string(); - return true; - } - default: - return false; - } -} void HelloResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(1, this->api_version_major); buffer.encode_uint32(2, this->api_version_minor); @@ -91,20 +53,6 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value return false; } } -void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); } -void ConnectRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->password); -} -bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->invalid_password = value.as_bool(); - return true; - } - default: - return false; - } -} void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); } void ConnectResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->invalid_password); @@ -171,108 +119,6 @@ void DeviceInfo::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->name); ProtoSize::add_uint32_field(total_size, 1, this->area_id); } -bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->uses_password = value.as_bool(); - return true; - } - case 7: { - this->has_deep_sleep = value.as_bool(); - return true; - } - case 10: { - this->webserver_port = value.as_uint32(); - return true; - } - case 11: { - this->legacy_bluetooth_proxy_version = value.as_uint32(); - return true; - } - case 15: { - this->bluetooth_proxy_feature_flags = value.as_uint32(); - return true; - } - case 14: { - this->legacy_voice_assistant_version = value.as_uint32(); - return true; - } - case 17: { - this->voice_assistant_feature_flags = value.as_uint32(); - return true; - } - case 19: { - this->api_encryption_supported = value.as_bool(); - return true; - } - default: - return false; - } -} -bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->name = value.as_string(); - return true; - } - case 3: { - this->mac_address = value.as_string(); - return true; - } - case 4: { - this->esphome_version = value.as_string(); - return true; - } - case 5: { - this->compilation_time = value.as_string(); - return true; - } - case 6: { - this->model = value.as_string(); - return true; - } - case 8: { - this->project_name = value.as_string(); - return true; - } - case 9: { - this->project_version = value.as_string(); - return true; - } - case 12: { - this->manufacturer = value.as_string(); - return true; - } - case 13: { - this->friendly_name = value.as_string(); - return true; - } - case 16: { - this->suggested_area = value.as_string(); - return true; - } - case 18: { - this->bluetooth_mac_address = value.as_string(); - return true; - } - case 20: { - this->devices.emplace_back(); - value.decode_to_message(this->devices.back()); - return true; - } - case 21: { - this->areas.emplace_back(); - value.decode_to_message(this->areas.back()); - return true; - } - case 22: { - value.decode_to_message(this->area); - return true; - } - default: - return false; - } -} void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->uses_password); buffer.encode_string(2, this->name); @@ -326,64 +172,6 @@ void DeviceInfoResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_message_object(total_size, 2, this->area); } #ifdef USE_BINARY_SENSOR -bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->is_status_binary_sensor = value.as_bool(); - return true; - } - case 7: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 9: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 10: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->device_class = value.as_string(); - return true; - } - case 8: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesBinarySensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -408,34 +196,6 @@ void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) cons ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = value.as_bool(); - return true; - } - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); @@ -450,76 +210,6 @@ void BinarySensorStateResponse::calculate_size(uint32_t &total_size) const { } #endif #ifdef USE_COVER -bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 5: { - this->assumed_state = value.as_bool(); - return true; - } - case 6: { - this->supports_position = value.as_bool(); - return true; - } - case 7: { - this->supports_tilt = value.as_bool(); - return true; - } - case 9: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 11: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 12: { - this->supports_stop = value.as_bool(); - return true; - } - case 13: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - case 10: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesCoverResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -550,42 +240,6 @@ void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->supports_stop); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->legacy_state = static_cast(value.as_uint32()); - return true; - } - case 5: { - this->current_operation = static_cast(value.as_uint32()); - return true; - } - case 6: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 3: { - this->position = value.as_float(); - return true; - } - case 4: { - this->tilt = value.as_float(); - return true; - } - default: - return false; - } -} void CoverStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_uint32(2, static_cast(this->legacy_state)); @@ -650,100 +304,8 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_legacy_command); - buffer.encode_uint32(3, static_cast(this->legacy_command)); - buffer.encode_bool(4, this->has_position); - buffer.encode_float(5, this->position); - buffer.encode_bool(6, this->has_tilt); - buffer.encode_float(7, this->tilt); - buffer.encode_bool(8, this->stop); - buffer.encode_uint32(9, this->device_id); -} -void CoverCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_legacy_command); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->legacy_command)); - ProtoSize::add_bool_field(total_size, 1, this->has_position); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_tilt); - ProtoSize::add_fixed_field<4>(total_size, 1, this->tilt != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->stop); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_FAN -bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 5: { - this->supports_oscillation = value.as_bool(); - return true; - } - case 6: { - this->supports_speed = value.as_bool(); - return true; - } - case 7: { - this->supports_direction = value.as_bool(); - return true; - } - case 8: { - this->supported_speed_count = value.as_int32(); - return true; - } - case 9: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 11: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 13: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 10: { - this->icon = value.as_string(); - return true; - } - case 12: { - this->supported_preset_modes.push_back(value.as_string()); - return true; - } - default: - return false; - } -} -bool ListEntitiesFanResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -780,56 +342,6 @@ void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const { } ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = value.as_bool(); - return true; - } - case 3: { - this->oscillating = value.as_bool(); - return true; - } - case 4: { - this->speed = static_cast(value.as_uint32()); - return true; - } - case 5: { - this->direction = static_cast(value.as_uint32()); - return true; - } - case 6: { - this->speed_level = value.as_int32(); - return true; - } - case 8: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool FanStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 7: { - this->preset_mode = value.as_string(); - return true; - } - default: - return false; - } -} -bool FanStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void FanStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); @@ -924,122 +436,8 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_state); - buffer.encode_bool(3, this->state); - buffer.encode_bool(4, this->has_speed); - buffer.encode_uint32(5, static_cast(this->speed)); - buffer.encode_bool(6, this->has_oscillating); - buffer.encode_bool(7, this->oscillating); - buffer.encode_bool(8, this->has_direction); - buffer.encode_uint32(9, static_cast(this->direction)); - buffer.encode_bool(10, this->has_speed_level); - buffer.encode_int32(11, this->speed_level); - buffer.encode_bool(12, this->has_preset_mode); - buffer.encode_string(13, this->preset_mode); - buffer.encode_uint32(14, this->device_id); -} -void FanCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_state); - ProtoSize::add_bool_field(total_size, 1, this->state); - ProtoSize::add_bool_field(total_size, 1, this->has_speed); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->speed)); - ProtoSize::add_bool_field(total_size, 1, this->has_oscillating); - ProtoSize::add_bool_field(total_size, 1, this->oscillating); - ProtoSize::add_bool_field(total_size, 1, this->has_direction); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->direction)); - ProtoSize::add_bool_field(total_size, 1, this->has_speed_level); - ProtoSize::add_int32_field(total_size, 1, this->speed_level); - ProtoSize::add_bool_field(total_size, 1, this->has_preset_mode); - ProtoSize::add_string_field(total_size, 1, this->preset_mode); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_LIGHT -bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 12: { - this->supported_color_modes.push_back(static_cast(value.as_uint32())); - return true; - } - case 5: { - this->legacy_supports_brightness = value.as_bool(); - return true; - } - case 6: { - this->legacy_supports_rgb = value.as_bool(); - return true; - } - case 7: { - this->legacy_supports_white_value = value.as_bool(); - return true; - } - case 8: { - this->legacy_supports_color_temperature = value.as_bool(); - return true; - } - case 13: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 15: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 16: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 11: { - this->effects.push_back(value.as_string()); - return true; - } - case 14: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesLightResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - case 9: { - this->min_mireds = value.as_float(); - return true; - } - case 10: { - this->max_mireds = value.as_float(); - return true; - } - default: - return false; - } -} void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -1088,80 +486,6 @@ void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 2, this->device_id); } -bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = value.as_bool(); - return true; - } - case 11: { - this->color_mode = static_cast(value.as_uint32()); - return true; - } - case 14: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool LightStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 9: { - this->effect = value.as_string(); - return true; - } - default: - return false; - } -} -bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 3: { - this->brightness = value.as_float(); - return true; - } - case 10: { - this->color_brightness = value.as_float(); - return true; - } - case 4: { - this->red = value.as_float(); - return true; - } - case 5: { - this->green = value.as_float(); - return true; - } - case 6: { - this->blue = value.as_float(); - return true; - } - case 7: { - this->white = value.as_float(); - return true; - } - case 8: { - this->color_temperature = value.as_float(); - return true; - } - case 12: { - this->cold_white = value.as_float(); - return true; - } - case 13: { - this->warm_white = value.as_float(); - return true; - } - default: - return false; - } -} void LightStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); @@ -1324,142 +648,8 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_state); - buffer.encode_bool(3, this->state); - buffer.encode_bool(4, this->has_brightness); - buffer.encode_float(5, this->brightness); - buffer.encode_bool(22, this->has_color_mode); - buffer.encode_uint32(23, static_cast(this->color_mode)); - buffer.encode_bool(20, this->has_color_brightness); - buffer.encode_float(21, this->color_brightness); - buffer.encode_bool(6, this->has_rgb); - buffer.encode_float(7, this->red); - buffer.encode_float(8, this->green); - buffer.encode_float(9, this->blue); - buffer.encode_bool(10, this->has_white); - buffer.encode_float(11, this->white); - buffer.encode_bool(12, this->has_color_temperature); - buffer.encode_float(13, this->color_temperature); - buffer.encode_bool(24, this->has_cold_white); - buffer.encode_float(25, this->cold_white); - buffer.encode_bool(26, this->has_warm_white); - buffer.encode_float(27, this->warm_white); - buffer.encode_bool(14, this->has_transition_length); - buffer.encode_uint32(15, this->transition_length); - buffer.encode_bool(16, this->has_flash_length); - buffer.encode_uint32(17, this->flash_length); - buffer.encode_bool(18, this->has_effect); - buffer.encode_string(19, this->effect); - buffer.encode_uint32(28, this->device_id); -} -void LightCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_state); - ProtoSize::add_bool_field(total_size, 1, this->state); - ProtoSize::add_bool_field(total_size, 1, this->has_brightness); - ProtoSize::add_fixed_field<4>(total_size, 1, this->brightness != 0.0f); - ProtoSize::add_bool_field(total_size, 2, this->has_color_mode); - ProtoSize::add_enum_field(total_size, 2, static_cast(this->color_mode)); - ProtoSize::add_bool_field(total_size, 2, this->has_color_brightness); - ProtoSize::add_fixed_field<4>(total_size, 2, this->color_brightness != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_rgb); - ProtoSize::add_fixed_field<4>(total_size, 1, this->red != 0.0f); - ProtoSize::add_fixed_field<4>(total_size, 1, this->green != 0.0f); - ProtoSize::add_fixed_field<4>(total_size, 1, this->blue != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_white); - ProtoSize::add_fixed_field<4>(total_size, 1, this->white != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_color_temperature); - ProtoSize::add_fixed_field<4>(total_size, 1, this->color_temperature != 0.0f); - ProtoSize::add_bool_field(total_size, 2, this->has_cold_white); - ProtoSize::add_fixed_field<4>(total_size, 2, this->cold_white != 0.0f); - ProtoSize::add_bool_field(total_size, 2, this->has_warm_white); - ProtoSize::add_fixed_field<4>(total_size, 2, this->warm_white != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_transition_length); - ProtoSize::add_uint32_field(total_size, 1, this->transition_length); - ProtoSize::add_bool_field(total_size, 2, this->has_flash_length); - ProtoSize::add_uint32_field(total_size, 2, this->flash_length); - ProtoSize::add_bool_field(total_size, 2, this->has_effect); - ProtoSize::add_string_field(total_size, 2, this->effect); - ProtoSize::add_uint32_field(total_size, 2, this->device_id); -} #endif #ifdef USE_SENSOR -bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 7: { - this->accuracy_decimals = value.as_int32(); - return true; - } - case 8: { - this->force_update = value.as_bool(); - return true; - } - case 10: { - this->state_class = static_cast(value.as_uint32()); - return true; - } - case 11: { - this->legacy_last_reset_type = static_cast(value.as_uint32()); - return true; - } - case 12: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 13: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 14: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 6: { - this->unit_of_measurement = value.as_string(); - return true; - } - case 9: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -1492,34 +682,6 @@ void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 2: { - this->state = value.as_float(); - return true; - } - default: - return false; - } -} void SensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_float(2, this->state); @@ -1534,64 +696,6 @@ void SensorStateResponse::calculate_size(uint32_t &total_size) const { } #endif #ifdef USE_SWITCH -bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->assumed_state = value.as_bool(); - return true; - } - case 7: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 8: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 10: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 9: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSwitchResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -1616,30 +720,6 @@ void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->device_class); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool SwitchStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = value.as_bool(); - return true; - } - case 3: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool SwitchStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); @@ -1674,72 +754,8 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->state); - buffer.encode_uint32(3, this->device_id); -} -void SwitchCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->state); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_TEXT_SENSOR -bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTextSensorResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -1762,40 +778,6 @@ void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const ProtoSize::add_string_field(total_size, 1, this->device_class); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->state = value.as_string(); - return true; - } - default: - return false; - } -} -bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->state); @@ -1823,38 +805,6 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return false; } } -void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, static_cast(this->level)); - buffer.encode_bool(2, this->dump_config); -} -void SubscribeLogsRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->level)); - ProtoSize::add_bool_field(total_size, 1, this->dump_config); -} -bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->level = static_cast(value.as_uint32()); - return true; - } - case 4: { - this->send_failed = value.as_bool(); - return true; - } - default: - return false; - } -} -bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 3: { - this->message = value.as_string(); - return true; - } - default: - return false; - } -} void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(1, static_cast(this->level)); buffer.encode_bytes(3, reinterpret_cast(this->message.data()), this->message.size()); @@ -1876,22 +826,6 @@ bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthD return false; } } -void NoiseEncryptionSetKeyRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_bytes(1, reinterpret_cast(this->key.data()), this->key.size()); -} -void NoiseEncryptionSetKeyRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->key); -} -bool NoiseEncryptionSetKeyResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->success = value.as_bool(); - return true; - } - default: - return false; - } -} void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); } void NoiseEncryptionSetKeyResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->success); @@ -1919,41 +853,6 @@ void HomeassistantServiceMap::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->key); ProtoSize::add_string_field(total_size, 1, this->value); } -bool HomeassistantServiceResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 5: { - this->is_event = value.as_bool(); - return true; - } - default: - return false; - } -} -bool HomeassistantServiceResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->service = value.as_string(); - return true; - } - case 2: { - this->data.emplace_back(); - value.decode_to_message(this->data.back()); - return true; - } - case 3: { - this->data_template.emplace_back(); - value.decode_to_message(this->data_template.back()); - return true; - } - case 4: { - this->variables.emplace_back(); - value.decode_to_message(this->variables.back()); - return true; - } - default: - return false; - } -} void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->service); for (auto &it : this->data) { @@ -1974,30 +873,6 @@ void HomeassistantServiceResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_repeated_message(total_size, 1, this->variables); ProtoSize::add_bool_field(total_size, 1, this->is_event); } -bool SubscribeHomeAssistantStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->once = value.as_bool(); - return true; - } - default: - return false; - } -} -bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->entity_id = value.as_string(); - return true; - } - case 2: { - this->attribute = value.as_string(); - return true; - } - default: - return false; - } -} void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->entity_id); buffer.encode_string(2, this->attribute); @@ -2026,16 +901,6 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel return false; } } -void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_string(1, this->entity_id); - buffer.encode_string(2, this->state); - buffer.encode_string(3, this->attribute); -} -void HomeAssistantStateResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->entity_id); - ProtoSize::add_string_field(total_size, 1, this->state); - ProtoSize::add_string_field(total_size, 1, this->attribute); -} bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { switch (field_id) { case 1: { @@ -2079,31 +944,6 @@ void ListEntitiesServicesArgument::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->name); ProtoSize::add_enum_field(total_size, 1, static_cast(this->type)); } -bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->name = value.as_string(); - return true; - } - case 3: { - this->args.emplace_back(); - value.decode_to_message(this->args.back()); - return true; - } - default: - return false; - } -} -bool ListEntitiesServicesResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->name); buffer.encode_fixed32(2, this->key); @@ -2235,68 +1075,8 @@ bool ExecuteServiceRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - for (auto &it : this->args) { - buffer.encode_message(2, it, true); - } -} -void ExecuteServiceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_repeated_message(total_size, 1, this->args); -} #endif #ifdef USE_CAMERA -bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 5: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 6: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesCameraResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -2317,40 +1097,6 @@ void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->done = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool CameraImageResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->data = value.as_string(); - return true; - } - default: - return false; - } -} -bool CameraImageResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void CameraImageResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bytes(2, reinterpret_cast(this->data.data()), this->data.size()); @@ -2377,138 +1123,8 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { return false; } } -void CameraImageRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_bool(1, this->single); - buffer.encode_bool(2, this->stream); -} -void CameraImageRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->single); - ProtoSize::add_bool_field(total_size, 1, this->stream); -} #endif #ifdef USE_CLIMATE -bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 5: { - this->supports_current_temperature = value.as_bool(); - return true; - } - case 6: { - this->supports_two_point_target_temperature = value.as_bool(); - return true; - } - case 7: { - this->supported_modes.push_back(static_cast(value.as_uint32())); - return true; - } - case 11: { - this->legacy_supports_away = value.as_bool(); - return true; - } - case 12: { - this->supports_action = value.as_bool(); - return true; - } - case 13: { - this->supported_fan_modes.push_back(static_cast(value.as_uint32())); - return true; - } - case 14: { - this->supported_swing_modes.push_back(static_cast(value.as_uint32())); - return true; - } - case 16: { - this->supported_presets.push_back(static_cast(value.as_uint32())); - return true; - } - case 18: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 20: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 22: { - this->supports_current_humidity = value.as_bool(); - return true; - } - case 23: { - this->supports_target_humidity = value.as_bool(); - return true; - } - case 26: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 15: { - this->supported_custom_fan_modes.push_back(value.as_string()); - return true; - } - case 17: { - this->supported_custom_presets.push_back(value.as_string()); - return true; - } - case 19: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesClimateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - case 8: { - this->visual_min_temperature = value.as_float(); - return true; - } - case 9: { - this->visual_max_temperature = value.as_float(); - return true; - } - case 10: { - this->visual_target_temperature_step = value.as_float(); - return true; - } - case 21: { - this->visual_current_temperature_step = value.as_float(); - return true; - } - case 24: { - this->visual_min_humidity = value.as_float(); - return true; - } - case 25: { - this->visual_max_humidity = value.as_float(); - return true; - } - default: - return false; - } -} void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -2601,88 +1217,6 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_max_humidity != 0.0f); ProtoSize::add_uint32_field(total_size, 2, this->device_id); } -bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->mode = static_cast(value.as_uint32()); - return true; - } - case 7: { - this->unused_legacy_away = value.as_bool(); - return true; - } - case 8: { - this->action = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->fan_mode = static_cast(value.as_uint32()); - return true; - } - case 10: { - this->swing_mode = static_cast(value.as_uint32()); - return true; - } - case 12: { - this->preset = static_cast(value.as_uint32()); - return true; - } - case 16: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 11: { - this->custom_fan_mode = value.as_string(); - return true; - } - case 13: { - this->custom_preset = value.as_string(); - return true; - } - default: - return false; - } -} -bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 3: { - this->current_temperature = value.as_float(); - return true; - } - case 4: { - this->target_temperature = value.as_float(); - return true; - } - case 5: { - this->target_temperature_low = value.as_float(); - return true; - } - case 6: { - this->target_temperature_high = value.as_float(); - return true; - } - case 14: { - this->current_humidity = value.as_float(); - return true; - } - case 15: { - this->target_humidity = value.as_float(); - return true; - } - default: - return false; - } -} void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_uint32(2, static_cast(this->mode)); @@ -2833,134 +1367,8 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_mode); - buffer.encode_uint32(3, static_cast(this->mode)); - buffer.encode_bool(4, this->has_target_temperature); - buffer.encode_float(5, this->target_temperature); - buffer.encode_bool(6, this->has_target_temperature_low); - buffer.encode_float(7, this->target_temperature_low); - buffer.encode_bool(8, this->has_target_temperature_high); - buffer.encode_float(9, this->target_temperature_high); - buffer.encode_bool(10, this->unused_has_legacy_away); - buffer.encode_bool(11, this->unused_legacy_away); - buffer.encode_bool(12, this->has_fan_mode); - buffer.encode_uint32(13, static_cast(this->fan_mode)); - buffer.encode_bool(14, this->has_swing_mode); - buffer.encode_uint32(15, static_cast(this->swing_mode)); - buffer.encode_bool(16, this->has_custom_fan_mode); - buffer.encode_string(17, this->custom_fan_mode); - buffer.encode_bool(18, this->has_preset); - buffer.encode_uint32(19, static_cast(this->preset)); - buffer.encode_bool(20, this->has_custom_preset); - buffer.encode_string(21, this->custom_preset); - buffer.encode_bool(22, this->has_target_humidity); - buffer.encode_float(23, this->target_humidity); - buffer.encode_uint32(24, this->device_id); -} -void ClimateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_mode); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_low); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_low != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_target_temperature_high); - ProtoSize::add_fixed_field<4>(total_size, 1, this->target_temperature_high != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->unused_has_legacy_away); - ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away); - ProtoSize::add_bool_field(total_size, 1, this->has_fan_mode); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->fan_mode)); - ProtoSize::add_bool_field(total_size, 1, this->has_swing_mode); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->swing_mode)); - ProtoSize::add_bool_field(total_size, 2, this->has_custom_fan_mode); - ProtoSize::add_string_field(total_size, 2, this->custom_fan_mode); - ProtoSize::add_bool_field(total_size, 2, this->has_preset); - ProtoSize::add_enum_field(total_size, 2, static_cast(this->preset)); - ProtoSize::add_bool_field(total_size, 2, this->has_custom_preset); - ProtoSize::add_string_field(total_size, 2, this->custom_preset); - ProtoSize::add_bool_field(total_size, 2, this->has_target_humidity); - ProtoSize::add_fixed_field<4>(total_size, 2, this->target_humidity != 0.0f); - ProtoSize::add_uint32_field(total_size, 2, this->device_id); -} #endif #ifdef USE_NUMBER -bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 9: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 10: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 12: { - this->mode = static_cast(value.as_uint32()); - return true; - } - case 14: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 11: { - this->unit_of_measurement = value.as_string(); - return true; - } - case 13: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesNumberResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - case 6: { - this->min_value = value.as_float(); - return true; - } - case 7: { - this->max_value = value.as_float(); - return true; - } - case 8: { - this->step = value.as_float(); - return true; - } - default: - return false; - } -} void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -2993,34 +1401,6 @@ void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->device_class); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool NumberStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 2: { - this->state = value.as_float(); - return true; - } - default: - return false; - } -} void NumberStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_float(2, this->state); @@ -3057,72 +1437,8 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_float(2, this->state); - buffer.encode_uint32(3, this->device_id); -} -void NumberCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_fixed_field<4>(total_size, 1, this->state != 0.0f); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_SELECT -bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 7: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 8: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSelectResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 6: { - this->options.push_back(value.as_string()); - return true; - } - default: - return false; - } -} -bool ListEntitiesSelectResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -3151,40 +1467,6 @@ void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool SelectStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool SelectStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->state = value.as_string(); - return true; - } - default: - return false; - } -} -bool SelectStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void SelectStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->state); @@ -3227,80 +1509,8 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_string(2, this->state); - buffer.encode_uint32(3, this->device_id); -} -void SelectCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_string_field(total_size, 1, this->state); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_SIREN -bool ListEntitiesSirenResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 8: { - this->supports_duration = value.as_bool(); - return true; - } - case 9: { - this->supports_volume = value.as_bool(); - return true; - } - case 10: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 11: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesSirenResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 7: { - this->tones.push_back(value.as_string()); - return true; - } - default: - return false; - } -} -bool ListEntitiesSirenResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -3333,30 +1543,6 @@ void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool SirenStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = value.as_bool(); - return true; - } - case 3: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool SirenStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void SirenStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); @@ -3425,98 +1611,8 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void SirenCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_state); - buffer.encode_bool(3, this->state); - buffer.encode_bool(4, this->has_tone); - buffer.encode_string(5, this->tone); - buffer.encode_bool(6, this->has_duration); - buffer.encode_uint32(7, this->duration); - buffer.encode_bool(8, this->has_volume); - buffer.encode_float(9, this->volume); - buffer.encode_uint32(10, this->device_id); -} -void SirenCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_state); - ProtoSize::add_bool_field(total_size, 1, this->state); - ProtoSize::add_bool_field(total_size, 1, this->has_tone); - ProtoSize::add_string_field(total_size, 1, this->tone); - ProtoSize::add_bool_field(total_size, 1, this->has_duration); - ProtoSize::add_uint32_field(total_size, 1, this->duration); - ProtoSize::add_bool_field(total_size, 1, this->has_volume); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_LOCK -bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->assumed_state = value.as_bool(); - return true; - } - case 9: { - this->supports_open = value.as_bool(); - return true; - } - case 10: { - this->requires_code = value.as_bool(); - return true; - } - case 12: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesLockResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 11: { - this->code_format = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesLockResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -3545,30 +1641,6 @@ void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->code_format); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = static_cast(value.as_uint32()); - return true; - } - case 3: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool LockStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void LockStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_uint32(2, static_cast(this->state)); @@ -3617,76 +1689,8 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void LockCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, static_cast(this->command)); - buffer.encode_bool(3, this->has_code); - buffer.encode_string(4, this->code); - buffer.encode_uint32(5, this->device_id); -} -void LockCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); - ProtoSize::add_bool_field(total_size, 1, this->has_code); - ProtoSize::add_string_field(total_size, 1, this->code); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_BUTTON -bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesButtonResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesButtonResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -3729,14 +1733,6 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, this->device_id); -} -void ButtonCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_MEDIA_PLAYER bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -3785,65 +1781,6 @@ void MediaPlayerSupportedFormat::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->purpose)); ProtoSize::add_uint32_field(total_size, 1, this->sample_bytes); } -bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->supports_pause = value.as_bool(); - return true; - } - case 10: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 9: { - this->supported_formats.emplace_back(); - value.decode_to_message(this->supported_formats.back()); - return true; - } - default: - return false; - } -} -bool ListEntitiesMediaPlayerResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -3870,38 +1807,6 @@ void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const ProtoSize::add_repeated_message(total_size, 1, this->supported_formats); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool MediaPlayerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = static_cast(value.as_uint32()); - return true; - } - case 4: { - this->muted = value.as_bool(); - return true; - } - case 5: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool MediaPlayerStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 3: { - this->volume = value.as_float(); - return true; - } - default: - return false; - } -} void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_uint32(2, static_cast(this->state)); @@ -3974,30 +1879,6 @@ bool MediaPlayerCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value return false; } } -void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_command); - buffer.encode_uint32(3, static_cast(this->command)); - buffer.encode_bool(4, this->has_volume); - buffer.encode_float(5, this->volume); - buffer.encode_bool(6, this->has_media_url); - buffer.encode_string(7, this->media_url); - buffer.encode_bool(8, this->has_announcement); - buffer.encode_bool(9, this->announcement); - buffer.encode_uint32(10, this->device_id); -} -void MediaPlayerCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_command); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); - ProtoSize::add_bool_field(total_size, 1, this->has_volume); - ProtoSize::add_fixed_field<4>(total_size, 1, this->volume != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->has_media_url); - ProtoSize::add_string_field(total_size, 1, this->media_url); - ProtoSize::add_bool_field(total_size, 1, this->has_announcement); - ProtoSize::add_bool_field(total_size, 1, this->announcement); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_BLUETOOTH_PROXY bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4010,12 +1891,6 @@ bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id, return false; } } -void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, this->flags); -} -void SubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->flags); -} bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -4056,48 +1931,6 @@ void BluetoothServiceData::calculate_size(uint32_t &total_size) const { } ProtoSize::add_string_field(total_size, 1, this->data); } -bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 3: { - this->rssi = value.as_sint32(); - return true; - } - case 7: { - this->address_type = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->name = value.as_string(); - return true; - } - case 4: { - this->service_uuids.push_back(value.as_string()); - return true; - } - case 5: { - this->service_data.emplace_back(); - value.decode_to_message(this->service_data.back()); - return true; - } - case 6: { - this->manufacturer_data.emplace_back(); - value.decode_to_message(this->manufacturer_data.back()); - return true; - } - default: - return false; - } -} void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_bytes(2, reinterpret_cast(this->name.data()), this->name.size()); @@ -4166,17 +1999,6 @@ void BluetoothLERawAdvertisement::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 1, this->address_type); ProtoSize::add_string_field(total_size, 1, this->data); } -bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->advertisements.emplace_back(); - value.decode_to_message(this->advertisements.back()); - return true; - } - default: - return false; - } -} void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->advertisements) { buffer.encode_message(1, it, true); @@ -4207,40 +2029,6 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) return false; } } -void BluetoothDeviceRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, static_cast(this->request_type)); - buffer.encode_bool(3, this->has_address_type); - buffer.encode_uint32(4, this->address_type); -} -void BluetoothDeviceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->request_type)); - ProtoSize::add_bool_field(total_size, 1, this->has_address_type); - ProtoSize::add_uint32_field(total_size, 1, this->address_type); -} -bool BluetoothDeviceConnectionResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->connected = value.as_bool(); - return true; - } - case 3: { - this->mtu = value.as_uint32(); - return true; - } - case 4: { - this->error = value.as_int32(); - return true; - } - default: - return false; - } -} void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_bool(2, this->connected); @@ -4263,10 +2051,6 @@ bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarI return false; } } -void BluetoothGATTGetServicesRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); } -void BluetoothGATTGetServicesRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); -} bool BluetoothGATTDescriptor::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -4387,27 +2171,6 @@ void BluetoothGATTService::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 1, this->handle); ProtoSize::add_repeated_message(total_size, 1, this->characteristics); } -bool BluetoothGATTGetServicesResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - default: - return false; - } -} -bool BluetoothGATTGetServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->services.emplace_back(); - value.decode_to_message(this->services.back()); - return true; - } - default: - return false; - } -} void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); for (auto &it : this->services) { @@ -4418,16 +2181,6 @@ void BluetoothGATTGetServicesResponse::calculate_size(uint32_t &total_size) cons ProtoSize::add_uint64_field(total_size, 1, this->address); ProtoSize::add_repeated_message(total_size, 1, this->services); } -bool BluetoothGATTGetServicesDoneResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - default: - return false; - } -} void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); } @@ -4448,38 +2201,6 @@ bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt valu return false; } } -void BluetoothGATTReadRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, this->handle); -} -void BluetoothGATTReadRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_uint32_field(total_size, 1, this->handle); -} -bool BluetoothGATTReadResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->handle = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool BluetoothGATTReadResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 3: { - this->data = value.as_string(); - return true; - } - default: - return false; - } -} void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); @@ -4518,18 +2239,6 @@ bool BluetoothGATTWriteRequest::decode_length(uint32_t field_id, ProtoLengthDeli return false; } } -void BluetoothGATTWriteRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, this->handle); - buffer.encode_bool(3, this->response); - buffer.encode_bytes(4, reinterpret_cast(this->data.data()), this->data.size()); -} -void BluetoothGATTWriteRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_uint32_field(total_size, 1, this->handle); - ProtoSize::add_bool_field(total_size, 1, this->response); - ProtoSize::add_string_field(total_size, 1, this->data); -} bool BluetoothGATTReadDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -4544,14 +2253,6 @@ bool BluetoothGATTReadDescriptorRequest::decode_varint(uint32_t field_id, ProtoV return false; } } -void BluetoothGATTReadDescriptorRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, this->handle); -} -void BluetoothGATTReadDescriptorRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_uint32_field(total_size, 1, this->handle); -} bool BluetoothGATTWriteDescriptorRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -4576,16 +2277,6 @@ bool BluetoothGATTWriteDescriptorRequest::decode_length(uint32_t field_id, Proto return false; } } -void BluetoothGATTWriteDescriptorRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, this->handle); - buffer.encode_bytes(3, reinterpret_cast(this->data.data()), this->data.size()); -} -void BluetoothGATTWriteDescriptorRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_uint32_field(total_size, 1, this->handle); - ProtoSize::add_string_field(total_size, 1, this->data); -} bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -4604,40 +2295,6 @@ bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt va return false; } } -void BluetoothGATTNotifyRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint64(1, this->address); - buffer.encode_uint32(2, this->handle); - buffer.encode_bool(3, this->enable); -} -void BluetoothGATTNotifyRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint64_field(total_size, 1, this->address); - ProtoSize::add_uint32_field(total_size, 1, this->handle); - ProtoSize::add_bool_field(total_size, 1, this->enable); -} -bool BluetoothGATTNotifyDataResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->handle = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool BluetoothGATTNotifyDataResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 3: { - this->data = value.as_string(); - return true; - } - default: - return false; - } -} void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); @@ -4648,24 +2305,6 @@ void BluetoothGATTNotifyDataResponse::calculate_size(uint32_t &total_size) const ProtoSize::add_uint32_field(total_size, 1, this->handle); ProtoSize::add_string_field(total_size, 1, this->data); } -bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->free = value.as_uint32(); - return true; - } - case 2: { - this->limit = value.as_uint32(); - return true; - } - case 3: { - this->allocated.push_back(value.as_uint64()); - return true; - } - default: - return false; - } -} void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(1, this->free); buffer.encode_uint32(2, this->limit); @@ -4682,24 +2321,6 @@ void BluetoothConnectionsFreeResponse::calculate_size(uint32_t &total_size) cons } } } -bool BluetoothGATTErrorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->handle = value.as_uint32(); - return true; - } - case 3: { - this->error = value.as_int32(); - return true; - } - default: - return false; - } -} void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); @@ -4710,20 +2331,6 @@ void BluetoothGATTErrorResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 1, this->handle); ProtoSize::add_int32_field(total_size, 1, this->error); } -bool BluetoothGATTWriteResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->handle = value.as_uint32(); - return true; - } - default: - return false; - } -} void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); @@ -4732,20 +2339,6 @@ void BluetoothGATTWriteResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address); ProtoSize::add_uint32_field(total_size, 1, this->handle); } -bool BluetoothGATTNotifyResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->handle = value.as_uint32(); - return true; - } - default: - return false; - } -} void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_uint32(2, this->handle); @@ -4754,24 +2347,6 @@ void BluetoothGATTNotifyResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint64_field(total_size, 1, this->address); ProtoSize::add_uint32_field(total_size, 1, this->handle); } -bool BluetoothDevicePairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->paired = value.as_bool(); - return true; - } - case 3: { - this->error = value.as_int32(); - return true; - } - default: - return false; - } -} void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_bool(2, this->paired); @@ -4782,24 +2357,6 @@ void BluetoothDevicePairingResponse::calculate_size(uint32_t &total_size) const ProtoSize::add_bool_field(total_size, 1, this->paired); ProtoSize::add_int32_field(total_size, 1, this->error); } -bool BluetoothDeviceUnpairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->success = value.as_bool(); - return true; - } - case 3: { - this->error = value.as_int32(); - return true; - } - default: - return false; - } -} void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_bool(2, this->success); @@ -4810,24 +2367,6 @@ void BluetoothDeviceUnpairingResponse::calculate_size(uint32_t &total_size) cons ProtoSize::add_bool_field(total_size, 1, this->success); ProtoSize::add_int32_field(total_size, 1, this->error); } -bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->address = value.as_uint64(); - return true; - } - case 2: { - this->success = value.as_bool(); - return true; - } - case 3: { - this->error = value.as_int32(); - return true; - } - default: - return false; - } -} void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint64(1, this->address); buffer.encode_bool(2, this->success); @@ -4838,20 +2377,6 @@ void BluetoothDeviceClearCacheResponse::calculate_size(uint32_t &total_size) con ProtoSize::add_bool_field(total_size, 1, this->success); ProtoSize::add_int32_field(total_size, 1, this->error); } -bool BluetoothScannerStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->state = static_cast(value.as_uint32()); - return true; - } - case 2: { - this->mode = static_cast(value.as_uint32()); - return true; - } - default: - return false; - } -} void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(1, static_cast(this->state)); buffer.encode_uint32(2, static_cast(this->mode)); @@ -4870,12 +2395,6 @@ bool BluetoothScannerSetModeRequest::decode_varint(uint32_t field_id, ProtoVarIn return false; } } -void BluetoothScannerSetModeRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, static_cast(this->mode)); -} -void BluetoothScannerSetModeRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); -} #endif #ifdef USE_VOICE_ASSISTANT bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -4892,14 +2411,6 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn return false; } } -void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_bool(1, this->subscribe); - buffer.encode_uint32(2, this->flags); -} -void SubscribeVoiceAssistantRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_bool_field(total_size, 1, this->subscribe); - ProtoSize::add_uint32_field(total_size, 1, this->flags); -} bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 1: { @@ -4934,38 +2445,6 @@ void VoiceAssistantAudioSettings::calculate_size(uint32_t &total_size) const { ProtoSize::add_uint32_field(total_size, 1, this->auto_gain); ProtoSize::add_fixed_field<4>(total_size, 1, this->volume_multiplier != 0.0f); } -bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->start = value.as_bool(); - return true; - } - case 3: { - this->flags = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->conversation_id = value.as_string(); - return true; - } - case 4: { - value.decode_to_message(this->audio_settings); - return true; - } - case 5: { - this->wake_word_phrase = value.as_string(); - return true; - } - default: - return false; - } -} void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->start); buffer.encode_string(2, this->conversation_id); @@ -4994,14 +2473,6 @@ bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) return false; } } -void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, this->port); - buffer.encode_bool(2, this->error); -} -void VoiceAssistantResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_uint32_field(total_size, 1, this->port); - ProtoSize::add_bool_field(total_size, 1, this->error); -} bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { @@ -5045,16 +2516,6 @@ bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDe return false; } } -void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, static_cast(this->event_type)); - for (auto &it : this->data) { - buffer.encode_message(2, it, true); - } -} -void VoiceAssistantEventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type)); - ProtoSize::add_repeated_message(total_size, 1, this->data); -} bool VoiceAssistantAudio::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -5119,22 +2580,6 @@ bool VoiceAssistantTimerEventResponse::decode_length(uint32_t field_id, ProtoLen return false; } } -void VoiceAssistantTimerEventResponse::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, static_cast(this->event_type)); - buffer.encode_string(2, this->timer_id); - buffer.encode_string(3, this->name); - buffer.encode_uint32(4, this->total_seconds); - buffer.encode_uint32(5, this->seconds_left); - buffer.encode_bool(6, this->is_active); -} -void VoiceAssistantTimerEventResponse::calculate_size(uint32_t &total_size) const { - ProtoSize::add_enum_field(total_size, 1, static_cast(this->event_type)); - ProtoSize::add_string_field(total_size, 1, this->timer_id); - ProtoSize::add_string_field(total_size, 1, this->name); - ProtoSize::add_uint32_field(total_size, 1, this->total_seconds); - ProtoSize::add_uint32_field(total_size, 1, this->seconds_left); - ProtoSize::add_bool_field(total_size, 1, this->is_active); -} bool VoiceAssistantAnnounceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 4: { @@ -5163,28 +2608,6 @@ bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLength return false; } } -void VoiceAssistantAnnounceRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_string(1, this->media_id); - buffer.encode_string(2, this->text); - buffer.encode_string(3, this->preannounce_media_id); - buffer.encode_bool(4, this->start_conversation); -} -void VoiceAssistantAnnounceRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_string_field(total_size, 1, this->media_id); - ProtoSize::add_string_field(total_size, 1, this->text); - ProtoSize::add_string_field(total_size, 1, this->preannounce_media_id); - ProtoSize::add_bool_field(total_size, 1, this->start_conversation); -} -bool VoiceAssistantAnnounceFinished::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->success = value.as_bool(); - return true; - } - default: - return false; - } -} void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); } void VoiceAssistantAnnounceFinished::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->success); @@ -5223,31 +2646,6 @@ void VoiceAssistantWakeWord::calculate_size(uint32_t &total_size) const { } } } -bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->max_active_wake_words = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->available_wake_words.emplace_back(); - value.decode_to_message(this->available_wake_words.back()); - return true; - } - case 2: { - this->active_wake_words.push_back(value.as_string()); - return true; - } - default: - return false; - } -} void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->available_wake_words) { buffer.encode_message(1, it, true); @@ -5276,82 +2674,8 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt return false; } } -void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { - for (auto &it : this->active_wake_words) { - buffer.encode_string(1, it, true); - } -} -void VoiceAssistantSetConfiguration::calculate_size(uint32_t &total_size) const { - if (!this->active_wake_words.empty()) { - for (const auto &it : this->active_wake_words) { - ProtoSize::add_string_field_repeated(total_size, 1, it); - } - } -} #endif #ifdef USE_ALARM_CONTROL_PANEL -bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->supported_features = value.as_uint32(); - return true; - } - case 9: { - this->requires_code = value.as_bool(); - return true; - } - case 10: { - this->requires_code_to_arm = value.as_bool(); - return true; - } - case 11: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesAlarmControlPanelResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesAlarmControlPanelResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -5378,30 +2702,6 @@ void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size) ProtoSize::add_bool_field(total_size, 1, this->requires_code_to_arm); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool AlarmControlPanelStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->state = static_cast(value.as_uint32()); - return true; - } - case 3: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool AlarmControlPanelStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_uint32(2, static_cast(this->state)); @@ -5446,86 +2746,8 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit return false; } } -void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, static_cast(this->command)); - buffer.encode_string(3, this->code); - buffer.encode_uint32(4, this->device_id); -} -void AlarmControlPanelCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); - ProtoSize::add_string_field(total_size, 1, this->code); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_TEXT -bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->min_length = value.as_uint32(); - return true; - } - case 9: { - this->max_length = value.as_uint32(); - return true; - } - case 11: { - this->mode = static_cast(value.as_uint32()); - return true; - } - case 12: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTextResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 10: { - this->pattern = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTextResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -5554,40 +2776,6 @@ void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->mode)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool TextStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool TextStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->state = value.as_string(); - return true; - } - default: - return false; - } -} -bool TextStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void TextStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->state); @@ -5630,68 +2818,8 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void TextCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_string(2, this->state); - buffer.encode_uint32(3, this->device_id); -} -void TextCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_string_field(total_size, 1, this->state); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_DATETIME_DATE -bool ListEntitiesDateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesDateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesDateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -5712,42 +2840,6 @@ void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool DateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->missing_state = value.as_bool(); - return true; - } - case 3: { - this->year = value.as_uint32(); - return true; - } - case 4: { - this->month = value.as_uint32(); - return true; - } - case 5: { - this->day = value.as_uint32(); - return true; - } - case 6: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool DateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void DateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->missing_state); @@ -5796,72 +2888,8 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void DateCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, this->year); - buffer.encode_uint32(3, this->month); - buffer.encode_uint32(4, this->day); - buffer.encode_uint32(5, this->device_id); -} -void DateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_uint32_field(total_size, 1, this->year); - ProtoSize::add_uint32_field(total_size, 1, this->month); - ProtoSize::add_uint32_field(total_size, 1, this->day); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_DATETIME_TIME -bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -5882,42 +2910,6 @@ void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool TimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->missing_state = value.as_bool(); - return true; - } - case 3: { - this->hour = value.as_uint32(); - return true; - } - case 4: { - this->minute = value.as_uint32(); - return true; - } - case 5: { - this->second = value.as_uint32(); - return true; - } - case 6: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool TimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void TimeStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->missing_state); @@ -5966,80 +2958,8 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void TimeCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, this->hour); - buffer.encode_uint32(3, this->minute); - buffer.encode_uint32(4, this->second); - buffer.encode_uint32(5, this->device_id); -} -void TimeCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_uint32_field(total_size, 1, this->hour); - ProtoSize::add_uint32_field(total_size, 1, this->minute); - ProtoSize::add_uint32_field(total_size, 1, this->second); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_EVENT -bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 10: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - case 9: { - this->event_types.push_back(value.as_string()); - return true; - } - default: - return false; - } -} -bool ListEntitiesEventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -6070,36 +2990,6 @@ void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const { } ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool EventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool EventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 2: { - this->event_type = value.as_string(); - return true; - } - default: - return false; - } -} -bool EventResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void EventResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_string(2, this->event_type); @@ -6112,72 +3002,6 @@ void EventResponse::calculate_size(uint32_t &total_size) const { } #endif #ifdef USE_VALVE -bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->assumed_state = value.as_bool(); - return true; - } - case 10: { - this->supports_position = value.as_bool(); - return true; - } - case 11: { - this->supports_stop = value.as_bool(); - return true; - } - case 12: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesValveResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesValveResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -6206,34 +3030,6 @@ void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_bool_field(total_size, 1, this->supports_stop); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool ValveStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 3: { - this->current_operation = static_cast(value.as_uint32()); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ValveStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 2: { - this->position = value.as_float(); - return true; - } - default: - return false; - } -} void ValveStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_float(2, this->position); @@ -6278,72 +3074,8 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void ValveCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->has_position); - buffer.encode_float(3, this->position); - buffer.encode_bool(4, this->stop); - buffer.encode_uint32(5, this->device_id); -} -void ValveCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_bool_field(total_size, 1, this->has_position); - ProtoSize::add_fixed_field<4>(total_size, 1, this->position != 0.0f); - ProtoSize::add_bool_field(total_size, 1, this->stop); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_DATETIME_DATETIME -bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 8: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesDateTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesDateTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -6364,34 +3096,6 @@ void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_enum_field(total_size, 1, static_cast(this->entity_category)); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool DateTimeStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->missing_state = value.as_bool(); - return true; - } - case 4: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool DateTimeStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 3: { - this->epoch_seconds = value.as_fixed32(); - return true; - } - default: - return false; - } -} void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->missing_state); @@ -6428,72 +3132,8 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void DateTimeCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_fixed32(2, this->epoch_seconds); - buffer.encode_uint32(3, this->device_id); -} -void DateTimeCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_fixed_field<4>(total_size, 1, this->epoch_seconds != 0); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif #ifdef USE_UPDATE -bool ListEntitiesUpdateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 6: { - this->disabled_by_default = value.as_bool(); - return true; - } - case 7: { - this->entity_category = static_cast(value.as_uint32()); - return true; - } - case 9: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool ListEntitiesUpdateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 1: { - this->object_id = value.as_string(); - return true; - } - case 3: { - this->name = value.as_string(); - return true; - } - case 4: { - this->unique_id = value.as_string(); - return true; - } - case 5: { - this->icon = value.as_string(); - return true; - } - case 8: { - this->device_class = value.as_string(); - return true; - } - default: - return false; - } -} -bool ListEntitiesUpdateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 2: { - this->key = value.as_fixed32(); - return true; - } - default: - return false; - } -} void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->object_id); buffer.encode_fixed32(2, this->key); @@ -6516,68 +3156,6 @@ void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const { ProtoSize::add_string_field(total_size, 1, this->device_class); ProtoSize::add_uint32_field(total_size, 1, this->device_id); } -bool UpdateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 2: { - this->missing_state = value.as_bool(); - return true; - } - case 3: { - this->in_progress = value.as_bool(); - return true; - } - case 4: { - this->has_progress = value.as_bool(); - return true; - } - case 11: { - this->device_id = value.as_uint32(); - return true; - } - default: - return false; - } -} -bool UpdateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { - switch (field_id) { - case 6: { - this->current_version = value.as_string(); - return true; - } - case 7: { - this->latest_version = value.as_string(); - return true; - } - case 8: { - this->title = value.as_string(); - return true; - } - case 9: { - this->release_summary = value.as_string(); - return true; - } - case 10: { - this->release_url = value.as_string(); - return true; - } - default: - return false; - } -} -bool UpdateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { - switch (field_id) { - case 1: { - this->key = value.as_fixed32(); - return true; - } - case 5: { - this->progress = value.as_float(); - return true; - } - default: - return false; - } -} void UpdateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->missing_state); @@ -6628,16 +3206,6 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { return false; } } -void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const { - buffer.encode_fixed32(1, this->key); - buffer.encode_uint32(2, static_cast(this->command)); - buffer.encode_uint32(3, this->device_id); -} -void UpdateCommandRequest::calculate_size(uint32_t &total_size) const { - ProtoSize::add_fixed_field<4>(total_size, 1, this->key != 0); - ProtoSize::add_enum_field(total_size, 1, static_cast(this->command)); - ProtoSize::add_uint32_field(total_size, 1, this->device_id); -} #endif } // namespace api diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 6a95055c2b..3f2d4afad3 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -327,8 +327,6 @@ class HelloRequest : public ProtoMessage { std::string client_info{}; uint32_t api_version_major{0}; uint32_t api_version_minor{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -355,8 +353,6 @@ class HelloResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ConnectRequest : public ProtoMessage { public: @@ -366,8 +362,6 @@ class ConnectRequest : public ProtoMessage { const char *message_name() const override { return "connect_request"; } #endif std::string password{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -390,7 +384,6 @@ class ConnectResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class DisconnectRequest : public ProtoMessage { public: @@ -522,8 +515,6 @@ class DeviceInfoResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ListEntitiesRequest : public ProtoMessage { public: @@ -581,9 +572,6 @@ class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BinarySensorStateResponse : public StateResponseProtoMessage { public: @@ -601,8 +589,6 @@ class BinarySensorStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_COVER @@ -625,9 +611,6 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class CoverStateResponse : public StateResponseProtoMessage { public: @@ -647,8 +630,6 @@ class CoverStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class CoverCommandRequest : public CommandProtoMessage { public: @@ -664,8 +645,6 @@ class CoverCommandRequest : public CommandProtoMessage { bool has_tilt{false}; float tilt{0.0f}; bool stop{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -695,9 +674,6 @@ class ListEntitiesFanResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class FanStateResponse : public StateResponseProtoMessage { public: @@ -719,9 +695,6 @@ class FanStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class FanCommandRequest : public CommandProtoMessage { public: @@ -742,8 +715,6 @@ class FanCommandRequest : public CommandProtoMessage { int32_t speed_level{0}; bool has_preset_mode{false}; std::string preset_mode{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -777,9 +748,6 @@ class ListEntitiesLightResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class LightStateResponse : public StateResponseProtoMessage { public: @@ -807,9 +775,6 @@ class LightStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class LightCommandRequest : public CommandProtoMessage { public: @@ -844,8 +809,6 @@ class LightCommandRequest : public CommandProtoMessage { uint32_t flash_length{0}; bool has_effect{false}; std::string effect{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -877,9 +840,6 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SensorStateResponse : public StateResponseProtoMessage { public: @@ -897,8 +857,6 @@ class SensorStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_SWITCH @@ -918,9 +876,6 @@ class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SwitchStateResponse : public StateResponseProtoMessage { public: @@ -937,8 +892,6 @@ class SwitchStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SwitchCommandRequest : public CommandProtoMessage { public: @@ -948,8 +901,6 @@ class SwitchCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "switch_command_request"; } #endif bool state{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -975,9 +926,6 @@ class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class TextSensorStateResponse : public StateResponseProtoMessage { public: @@ -995,9 +943,6 @@ class TextSensorStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif class SubscribeLogsRequest : public ProtoMessage { @@ -1009,8 +954,6 @@ class SubscribeLogsRequest : public ProtoMessage { #endif enums::LogLevel level{}; bool dump_config{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1035,8 +978,6 @@ class SubscribeLogsResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #ifdef USE_API_NOISE class NoiseEncryptionSetKeyRequest : public ProtoMessage { @@ -1047,8 +988,6 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage { const char *message_name() const override { return "noise_encryption_set_key_request"; } #endif std::string key{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1071,7 +1010,6 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif class SubscribeHomeassistantServicesRequest : public ProtoMessage { @@ -1119,8 +1057,6 @@ class HomeassistantServiceResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SubscribeHomeAssistantStatesRequest : public ProtoMessage { public: @@ -1152,8 +1088,6 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class HomeAssistantStateResponse : public ProtoMessage { public: @@ -1165,8 +1099,6 @@ class HomeAssistantStateResponse : public ProtoMessage { std::string entity_id{}; std::string state{}; std::string attribute{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1236,8 +1168,6 @@ class ListEntitiesServicesResponse : public ProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; class ExecuteServiceArgument : public ProtoMessage { public: @@ -1270,8 +1200,6 @@ class ExecuteServiceRequest : public ProtoMessage { #endif uint32_t key{0}; std::vector args{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1296,9 +1224,6 @@ class ListEntitiesCameraResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class CameraImageResponse : public StateResponseProtoMessage { public: @@ -1316,9 +1241,6 @@ class CameraImageResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class CameraImageRequest : public ProtoMessage { public: @@ -1329,8 +1251,6 @@ class CameraImageRequest : public ProtoMessage { #endif bool single{false}; bool stream{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1372,9 +1292,6 @@ class ListEntitiesClimateResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ClimateStateResponse : public StateResponseProtoMessage { public: @@ -1404,9 +1321,6 @@ class ClimateStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ClimateCommandRequest : public CommandProtoMessage { public: @@ -1437,8 +1351,6 @@ class ClimateCommandRequest : public CommandProtoMessage { std::string custom_preset{}; bool has_target_humidity{false}; float target_humidity{0.0f}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1470,9 +1382,6 @@ class ListEntitiesNumberResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class NumberStateResponse : public StateResponseProtoMessage { public: @@ -1490,8 +1399,6 @@ class NumberStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class NumberCommandRequest : public CommandProtoMessage { public: @@ -1501,8 +1408,6 @@ class NumberCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "number_command_request"; } #endif float state{0.0f}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1528,9 +1433,6 @@ class ListEntitiesSelectResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SelectStateResponse : public StateResponseProtoMessage { public: @@ -1548,9 +1450,6 @@ class SelectStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SelectCommandRequest : public CommandProtoMessage { public: @@ -1560,8 +1459,6 @@ class SelectCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "select_command_request"; } #endif std::string state{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1590,9 +1487,6 @@ class ListEntitiesSirenResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SirenStateResponse : public StateResponseProtoMessage { public: @@ -1609,8 +1503,6 @@ class SirenStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SirenCommandRequest : public CommandProtoMessage { public: @@ -1627,8 +1519,6 @@ class SirenCommandRequest : public CommandProtoMessage { uint32_t duration{0}; bool has_volume{false}; float volume{0.0f}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1658,9 +1548,6 @@ class ListEntitiesLockResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class LockStateResponse : public StateResponseProtoMessage { public: @@ -1677,8 +1564,6 @@ class LockStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class LockCommandRequest : public CommandProtoMessage { public: @@ -1690,8 +1575,6 @@ class LockCommandRequest : public CommandProtoMessage { enums::LockCommand command{}; bool has_code{false}; std::string code{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1718,9 +1601,6 @@ class ListEntitiesButtonResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ButtonCommandRequest : public CommandProtoMessage { public: @@ -1729,8 +1609,6 @@ class ButtonCommandRequest : public CommandProtoMessage { #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "button_command_request"; } #endif - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1774,9 +1652,6 @@ class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class MediaPlayerStateResponse : public StateResponseProtoMessage { public: @@ -1795,8 +1670,6 @@ class MediaPlayerStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class MediaPlayerCommandRequest : public CommandProtoMessage { public: @@ -1813,8 +1686,6 @@ class MediaPlayerCommandRequest : public CommandProtoMessage { std::string media_url{}; bool has_announcement{false}; bool announcement{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1834,8 +1705,6 @@ class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { const char *message_name() const override { return "subscribe_bluetooth_le_advertisements_request"; } #endif uint32_t flags{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1879,8 +1748,6 @@ class BluetoothLEAdvertisementResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothLERawAdvertisement : public ProtoMessage { public: @@ -1913,7 +1780,6 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; class BluetoothDeviceRequest : public ProtoMessage { public: @@ -1926,8 +1792,6 @@ class BluetoothDeviceRequest : public ProtoMessage { enums::BluetoothDeviceRequestType request_type{}; bool has_address_type{false}; uint32_t address_type{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -1953,7 +1817,6 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTGetServicesRequest : public ProtoMessage { public: @@ -1963,8 +1826,6 @@ class BluetoothGATTGetServicesRequest : public ProtoMessage { const char *message_name() const override { return "bluetooth_gatt_get_services_request"; } #endif uint64_t address{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2032,8 +1893,6 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { public: @@ -2050,7 +1909,6 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTReadRequest : public ProtoMessage { public: @@ -2061,8 +1919,6 @@ class BluetoothGATTReadRequest : public ProtoMessage { #endif uint64_t address{0}; uint32_t handle{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2087,8 +1943,6 @@ class BluetoothGATTReadResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTWriteRequest : public ProtoMessage { public: @@ -2101,8 +1955,6 @@ class BluetoothGATTWriteRequest : public ProtoMessage { uint32_t handle{0}; bool response{false}; std::string data{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2120,8 +1972,6 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage { #endif uint64_t address{0}; uint32_t handle{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2139,8 +1989,6 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage { uint64_t address{0}; uint32_t handle{0}; std::string data{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2159,8 +2007,6 @@ class BluetoothGATTNotifyRequest : public ProtoMessage { uint64_t address{0}; uint32_t handle{0}; bool enable{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2185,8 +2031,6 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage { public: @@ -2218,7 +2062,6 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTErrorResponse : public ProtoMessage { public: @@ -2237,7 +2080,6 @@ class BluetoothGATTErrorResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTWriteResponse : public ProtoMessage { public: @@ -2255,7 +2097,6 @@ class BluetoothGATTWriteResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothGATTNotifyResponse : public ProtoMessage { public: @@ -2273,7 +2114,6 @@ class BluetoothGATTNotifyResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothDevicePairingResponse : public ProtoMessage { public: @@ -2292,7 +2132,6 @@ class BluetoothDevicePairingResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothDeviceUnpairingResponse : public ProtoMessage { public: @@ -2311,7 +2150,6 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { public: @@ -2343,7 +2181,6 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothScannerStateResponse : public ProtoMessage { public: @@ -2361,7 +2198,6 @@ class BluetoothScannerStateResponse : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class BluetoothScannerSetModeRequest : public ProtoMessage { public: @@ -2371,8 +2207,6 @@ class BluetoothScannerSetModeRequest : public ProtoMessage { const char *message_name() const override { return "bluetooth_scanner_set_mode_request"; } #endif enums::BluetoothScannerMode mode{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2391,8 +2225,6 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage { #endif bool subscribe{false}; uint32_t flags{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2434,8 +2266,6 @@ class VoiceAssistantRequest : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantResponse : public ProtoMessage { public: @@ -2446,8 +2276,6 @@ class VoiceAssistantResponse : public ProtoMessage { #endif uint32_t port{0}; bool error{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2477,8 +2305,6 @@ class VoiceAssistantEventResponse : public ProtoMessage { #endif enums::VoiceAssistantEvent event_type{}; std::vector data{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2519,8 +2345,6 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage { uint32_t total_seconds{0}; uint32_t seconds_left{0}; bool is_active{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2540,8 +2364,6 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage { std::string text{}; std::string preannounce_media_id{}; bool start_conversation{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2565,7 +2387,6 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantWakeWord : public ProtoMessage { public: @@ -2611,8 +2432,6 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { #endif protected: - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: @@ -2622,8 +2441,6 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { const char *message_name() const override { return "voice_assistant_set_configuration"; } #endif std::vector active_wake_words{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2650,9 +2467,6 @@ class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class AlarmControlPanelStateResponse : public StateResponseProtoMessage { public: @@ -2669,8 +2483,6 @@ class AlarmControlPanelStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class AlarmControlPanelCommandRequest : public CommandProtoMessage { public: @@ -2681,8 +2493,6 @@ class AlarmControlPanelCommandRequest : public CommandProtoMessage { #endif enums::AlarmControlPanelStateCommand command{}; std::string code{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2712,9 +2522,6 @@ class ListEntitiesTextResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class TextStateResponse : public StateResponseProtoMessage { public: @@ -2732,9 +2539,6 @@ class TextStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class TextCommandRequest : public CommandProtoMessage { public: @@ -2744,8 +2548,6 @@ class TextCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "text_command_request"; } #endif std::string state{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2771,9 +2573,6 @@ class ListEntitiesDateResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class DateStateResponse : public StateResponseProtoMessage { public: @@ -2793,8 +2592,6 @@ class DateStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class DateCommandRequest : public CommandProtoMessage { public: @@ -2806,8 +2603,6 @@ class DateCommandRequest : public CommandProtoMessage { uint32_t year{0}; uint32_t month{0}; uint32_t day{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2832,9 +2627,6 @@ class ListEntitiesTimeResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class TimeStateResponse : public StateResponseProtoMessage { public: @@ -2854,8 +2646,6 @@ class TimeStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class TimeCommandRequest : public CommandProtoMessage { public: @@ -2867,8 +2657,6 @@ class TimeCommandRequest : public CommandProtoMessage { uint32_t hour{0}; uint32_t minute{0}; uint32_t second{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2895,9 +2683,6 @@ class ListEntitiesEventResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class EventResponse : public StateResponseProtoMessage { public: @@ -2914,9 +2699,6 @@ class EventResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; #endif #ifdef USE_VALVE @@ -2938,9 +2720,6 @@ class ListEntitiesValveResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ValveStateResponse : public StateResponseProtoMessage { public: @@ -2958,8 +2737,6 @@ class ValveStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class ValveCommandRequest : public CommandProtoMessage { public: @@ -2971,8 +2748,6 @@ class ValveCommandRequest : public CommandProtoMessage { bool has_position{false}; float position{0.0f}; bool stop{false}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -2997,9 +2772,6 @@ class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class DateTimeStateResponse : public StateResponseProtoMessage { public: @@ -3017,8 +2789,6 @@ class DateTimeStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class DateTimeCommandRequest : public CommandProtoMessage { public: @@ -3028,8 +2798,6 @@ class DateTimeCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "date_time_command_request"; } #endif uint32_t epoch_seconds{0}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif @@ -3055,9 +2823,6 @@ class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class UpdateStateResponse : public StateResponseProtoMessage { public: @@ -3082,9 +2847,6 @@ class UpdateStateResponse : public StateResponseProtoMessage { #endif protected: - bool decode_32bit(uint32_t field_id, Proto32Bit value) override; - bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class UpdateCommandRequest : public CommandProtoMessage { public: @@ -3094,8 +2856,6 @@ class UpdateCommandRequest : public CommandProtoMessage { const char *message_name() const override { return "update_command_request"; } #endif enums::UpdateCommand command{}; - void encode(ProtoWriteBuffer buffer) const override; - void calculate_size(uint32_t &total_size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 2482521398..3ae1b195e4 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -1059,6 +1059,11 @@ def build_message_type( # Get message ID if it's a service message message_id: int | None = get_opt(desc, pb.id) + # Get source direction to determine if we need decode/encode methods + source: int = get_opt(desc, pb.source, SOURCE_BOTH) + needs_decode = source in (SOURCE_BOTH, SOURCE_CLIENT) + needs_encode = source in (SOURCE_BOTH, SOURCE_SERVER) + # Add MESSAGE_TYPE method if this is a service message if message_id is not None: # Validate that message_id fits in uint8_t @@ -1101,18 +1106,21 @@ def build_message_type( protected_content.extend(ti.protected_content) public_content.extend(ti.public_content) - # Always include encode/decode logic for all fields - encode.append(ti.encode_content) - size_calc.append(ti.get_size_calculation(f"this->{ti.field_name}")) + # Only collect encode logic if this message needs it + if needs_encode: + encode.append(ti.encode_content) + size_calc.append(ti.get_size_calculation(f"this->{ti.field_name}")) - if ti.decode_varint_content: - decode_varint.append(ti.decode_varint_content) - if ti.decode_length_content: - decode_length.append(ti.decode_length_content) - if ti.decode_32bit_content: - decode_32bit.append(ti.decode_32bit_content) - if ti.decode_64bit_content: - decode_64bit.append(ti.decode_64bit_content) + # Only collect decode methods if this message needs them + if needs_decode: + if ti.decode_varint_content: + decode_varint.append(ti.decode_varint_content) + if ti.decode_length_content: + decode_length.append(ti.decode_length_content) + if ti.decode_32bit_content: + decode_32bit.append(ti.decode_32bit_content) + if ti.decode_64bit_content: + decode_64bit.append(ti.decode_64bit_content) if ti.dump_content: dump.append(ti.dump_content) @@ -1158,8 +1166,8 @@ def build_message_type( prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;" protected_content.insert(0, prot) - # Only generate encode method if there are fields to encode - if encode: + # Only generate encode method if this message needs encoding and has fields + if needs_encode and encode: o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{" if len(encode) == 1 and len(encode[0]) + len(o) + 3 < 120: o += f" {encode[0]} " @@ -1170,10 +1178,10 @@ def build_message_type( cpp += o prot = "void encode(ProtoWriteBuffer buffer) const override;" public_content.append(prot) - # If no fields to encode, the default implementation in ProtoMessage will be used + # If no fields to encode or message doesn't need encoding, the default implementation in ProtoMessage will be used - # Add calculate_size method only if there are fields - if size_calc: + # Add calculate_size method only if this message needs encoding and has fields + if needs_encode and size_calc: o = f"void {desc.name}::calculate_size(uint32_t &total_size) const {{" # For a single field, just inline it for simplicity if len(size_calc) == 1 and len(size_calc[0]) + len(o) + 3 < 120: @@ -1186,7 +1194,7 @@ def build_message_type( cpp += o prot = "void calculate_size(uint32_t &total_size) const override;" public_content.append(prot) - # If no fields to calculate size for, the default implementation in ProtoMessage will be used + # If no fields to calculate size for or message doesn't need encoding, the default implementation in ProtoMessage will be used # dump_to method declaration in header prot = "#ifdef HAS_PROTO_MESSAGE_DUMP\n" From 01b4e214b9290dff0b036729d8a13ab89528f67c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 13 Jul 2025 08:59:49 +1000 Subject: [PATCH 21/90] [usb_uart] Be flexible about descriptor layout for CDC-ACM devices (#9425) --- .../components/usb_host/usb_host_client.cpp | 20 ++--- esphome/components/usb_uart/cp210x.cpp | 2 +- esphome/components/usb_uart/usb_uart.cpp | 85 +++++++++---------- esphome/components/usb_uart/usb_uart.h | 7 +- 4 files changed, 56 insertions(+), 58 deletions(-) diff --git a/esphome/components/usb_host/usb_host_client.cpp b/esphome/components/usb_host/usb_host_client.cpp index fc5f772f41..edf6c94b07 100644 --- a/esphome/components/usb_host/usb_host_client.cpp +++ b/esphome/components/usb_host/usb_host_client.cpp @@ -70,7 +70,7 @@ static void usbh_print_cfg_desc(const usb_config_desc_t *cfg_desc) { ESP_LOGV(TAG, "bMaxPower %dmA", cfg_desc->bMaxPower * 2); } -void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) { +static void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) { if (devc_desc == NULL) { return; } @@ -92,8 +92,8 @@ void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) { ESP_LOGV(TAG, "bNumConfigurations %d", devc_desc->bNumConfigurations); } -void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc, - print_class_descriptor_cb class_specific_cb) { +static void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc, + print_class_descriptor_cb class_specific_cb) { if (cfg_desc == nullptr) { return; } @@ -128,9 +128,9 @@ void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc, static std::string get_descriptor_string(const usb_str_desc_t *desc) { char buffer[256]; if (desc == nullptr) - return "(unknown)"; + return "(unspecified)"; char *p = buffer; - for (size_t i = 0; i != desc->bLength / 2; i++) { + for (int i = 0; i != desc->bLength / 2; i++) { auto c = desc->wData[i]; if (c < 0x100) *p++ = static_cast(c); @@ -169,7 +169,7 @@ void USBClient::setup() { this->mark_failed(); return; } - for (auto trq : this->trq_pool_) { + for (auto *trq : this->trq_pool_) { usb_host_transfer_alloc(64, 0, &trq->transfer); trq->client = this; } @@ -197,7 +197,8 @@ void USBClient::loop() { ESP_LOGD(TAG, "Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct); if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) { usb_device_info_t dev_info; - if ((err = usb_host_device_info(this->device_handle_, &dev_info)) != ESP_OK) { + err = usb_host_device_info(this->device_handle_, &dev_info); + if (err != ESP_OK) { ESP_LOGW(TAG, "Device info failed: %s", esp_err_to_name(err)); this->disconnect(); break; @@ -336,7 +337,7 @@ static void transfer_callback(usb_transfer_t *xfer) { * @throws None. */ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) { - auto trq = this->get_trq_(); + auto *trq = this->get_trq_(); if (trq == nullptr) { ESP_LOGE(TAG, "Too many requests queued"); return; @@ -349,7 +350,6 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err); this->release_trq(trq); - this->disconnect(); } } @@ -364,7 +364,7 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u * @throws None. */ void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) { - auto trq = this->get_trq_(); + auto *trq = this->get_trq_(); if (trq == nullptr) { ESP_LOGE(TAG, "Too many requests queued"); return; diff --git a/esphome/components/usb_uart/cp210x.cpp b/esphome/components/usb_uart/cp210x.cpp index 267385d1bd..f7d60c307a 100644 --- a/esphome/components/usb_uart/cp210x.cpp +++ b/esphome/components/usb_uart/cp210x.cpp @@ -43,7 +43,7 @@ static constexpr uint8_t SET_BAUDRATE = 0x1E; // Set the baud rate. static constexpr uint8_t SET_CHARS = 0x19; // Set special characters. static constexpr uint8_t VENDOR_SPECIFIC = 0xFF; // Vendor specific command. -std::vector USBUartTypeCP210X::parse_descriptors_(usb_device_handle_t dev_hdl) { +std::vector USBUartTypeCP210X::parse_descriptors(usb_device_handle_t dev_hdl) { const usb_config_desc_t *config_desc; const usb_device_desc_t *device_desc; int conf_offset = 0, ep_offset; diff --git a/esphome/components/usb_uart/usb_uart.cpp b/esphome/components/usb_uart/usb_uart.cpp index e599409f0c..934306f480 100644 --- a/esphome/components/usb_uart/usb_uart.cpp +++ b/esphome/components/usb_uart/usb_uart.cpp @@ -18,52 +18,48 @@ namespace usb_uart { */ static optional get_cdc(const usb_config_desc_t *config_desc, uint8_t intf_idx) { int conf_offset, ep_offset; - const usb_ep_desc_t *notify_ep{}, *in_ep{}, *out_ep{}; - uint8_t interface_number = 0; - // look for an interface with one interrupt endpoint (notify), and an interface with two bulk endpoints (data in/out) + // look for an interface with an interrupt endpoint (notify), and one with two bulk endpoints (data in/out) + CdcEps eps{}; + eps.bulk_interface_number = 0xFF; for (;;) { - auto intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset); + const auto *intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset); if (!intf_desc) { ESP_LOGE(TAG, "usb_parse_interface_descriptor failed"); return nullopt; } - if (intf_desc->bNumEndpoints == 1) { + ESP_LOGD(TAG, "intf_desc: bInterfaceClass=%02X, bInterfaceSubClass=%02X, bInterfaceProtocol=%02X, bNumEndpoints=%d", + intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol, + intf_desc->bNumEndpoints); + for (uint8_t i = 0; i != intf_desc->bNumEndpoints; i++) { ep_offset = conf_offset; - notify_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset); - if (!notify_ep) { - ESP_LOGE(TAG, "notify_ep: usb_parse_endpoint_descriptor_by_index failed"); + const auto *ep = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &ep_offset); + if (!ep) { + ESP_LOGE(TAG, "Ran out of interfaces at %d before finding all endpoints", i); return nullopt; } - if (notify_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_INT) - notify_ep = nullptr; - } else if (USB_CLASS_CDC_DATA && intf_desc->bNumEndpoints == 2) { - interface_number = intf_desc->bInterfaceNumber; - ep_offset = conf_offset; - out_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset); - if (!out_ep) { - ESP_LOGE(TAG, "out_ep: usb_parse_endpoint_descriptor_by_index failed"); - return nullopt; + ESP_LOGD(TAG, "ep: bEndpointAddress=%02X, bmAttributes=%02X", ep->bEndpointAddress, ep->bmAttributes); + if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_INT) { + eps.notify_ep = ep; + eps.interrupt_interface_number = intf_desc->bInterfaceNumber; + } else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && ep->bEndpointAddress & usb_host::USB_DIR_IN && + (eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) { + eps.in_ep = ep; + eps.bulk_interface_number = intf_desc->bInterfaceNumber; + } else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && !(ep->bEndpointAddress & usb_host::USB_DIR_IN) && + (eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) { + eps.out_ep = ep; + eps.bulk_interface_number = intf_desc->bInterfaceNumber; + } else { + ESP_LOGE(TAG, "Unexpected endpoint attributes: %02X", ep->bmAttributes); + continue; } - if (out_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK) - out_ep = nullptr; - ep_offset = conf_offset; - in_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &ep_offset); - if (!in_ep) { - ESP_LOGE(TAG, "in_ep: usb_parse_endpoint_descriptor_by_index failed"); - return nullopt; - } - if (in_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK) - in_ep = nullptr; } - if (in_ep != nullptr && out_ep != nullptr && notify_ep != nullptr) - break; + if (eps.in_ep != nullptr && eps.out_ep != nullptr && eps.notify_ep != nullptr) + return eps; } - if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN) - return CdcEps{notify_ep, in_ep, out_ep, interface_number}; - return CdcEps{notify_ep, out_ep, in_ep, interface_number}; } -std::vector USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t dev_hdl) { +std::vector USBUartTypeCdcAcm::parse_descriptors(usb_device_handle_t dev_hdl) { const usb_config_desc_t *config_desc; const usb_device_desc_t *device_desc; int desc_offset = 0; @@ -78,7 +74,7 @@ std::vector USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t de ESP_LOGE(TAG, "get_active_config_descriptor failed"); return {}; } - if (device_desc->bDeviceClass == USB_CLASS_COMM) { + if (device_desc->bDeviceClass == USB_CLASS_COMM || device_desc->bDeviceClass == USB_CLASS_VENDOR_SPEC) { // single CDC-ACM device if (auto eps = get_cdc(config_desc, 0)) { ESP_LOGV(TAG, "Found CDC-ACM device"); @@ -194,7 +190,7 @@ void USBUartComponent::start_input(USBUartChannel *channel) { if (!channel->initialised_ || channel->input_started_ || channel->input_buffer_.get_free_space() < channel->cdc_dev_.in_ep->wMaxPacketSize) return; - auto ep = channel->cdc_dev_.in_ep; + const auto *ep = channel->cdc_dev_.in_ep; auto callback = [this, channel](const usb_host::TransferStatus &status) { ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code); if (!status.success) { @@ -227,7 +223,7 @@ void USBUartComponent::start_output(USBUartChannel *channel) { if (channel->output_buffer_.is_empty()) { return; } - auto ep = channel->cdc_dev_.out_ep; + const auto *ep = channel->cdc_dev_.out_ep; auto callback = [this, channel](const usb_host::TransferStatus &status) { ESP_LOGV(TAG, "Output Transfer result: length: %u; status %X", status.data_len, status.error_code); channel->output_started_ = false; @@ -259,15 +255,15 @@ static void fix_mps(const usb_ep_desc_t *ep) { } } void USBUartTypeCdcAcm::on_connected() { - auto cdc_devs = this->parse_descriptors_(this->device_handle_); + auto cdc_devs = this->parse_descriptors(this->device_handle_); if (cdc_devs.empty()) { this->status_set_error("No CDC-ACM device found"); this->disconnect(); return; } ESP_LOGD(TAG, "Found %zu CDC-ACM devices", cdc_devs.size()); - auto i = 0; - for (auto channel : this->channels_) { + size_t i = 0; + for (auto *channel : this->channels_) { if (i == cdc_devs.size()) { ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_); this->status_set_warning("No configuration found for channel"); @@ -277,10 +273,11 @@ void USBUartTypeCdcAcm::on_connected() { fix_mps(channel->cdc_dev_.in_ep); fix_mps(channel->cdc_dev_.out_ep); channel->initialised_ = true; - auto err = usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number, 0); + auto err = + usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number, 0); if (err != ESP_OK) { ESP_LOGE(TAG, "usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_, - channel->cdc_dev_.interface_number); + channel->cdc_dev_.bulk_interface_number); this->status_set_error("usb_host_interface_claim failed"); this->disconnect(); return; @@ -290,7 +287,7 @@ void USBUartTypeCdcAcm::on_connected() { } void USBUartTypeCdcAcm::on_disconnected() { - for (auto channel : this->channels_) { + for (auto *channel : this->channels_) { if (channel->cdc_dev_.in_ep != nullptr) { usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress); usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress); @@ -303,7 +300,7 @@ void USBUartTypeCdcAcm::on_disconnected() { usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress); usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress); } - usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number); + usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number); channel->initialised_ = false; channel->input_started_ = false; channel->output_started_ = false; @@ -314,7 +311,7 @@ void USBUartTypeCdcAcm::on_disconnected() { } void USBUartTypeCdcAcm::enable_channels() { - for (auto channel : this->channels_) { + for (auto *channel : this->channels_) { if (!channel->initialised_) continue; channel->input_started_ = false; diff --git a/esphome/components/usb_uart/usb_uart.h b/esphome/components/usb_uart/usb_uart.h index fd0fb2c59a..a103c51add 100644 --- a/esphome/components/usb_uart/usb_uart.h +++ b/esphome/components/usb_uart/usb_uart.h @@ -25,7 +25,8 @@ struct CdcEps { const usb_ep_desc_t *notify_ep; const usb_ep_desc_t *in_ep; const usb_ep_desc_t *out_ep; - uint8_t interface_number; + uint8_t bulk_interface_number; + uint8_t interrupt_interface_number; }; enum UARTParityOptions { @@ -123,7 +124,7 @@ class USBUartTypeCdcAcm : public USBUartComponent { USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {} protected: - virtual std::vector parse_descriptors_(usb_device_handle_t dev_hdl); + virtual std::vector parse_descriptors(usb_device_handle_t dev_hdl); void on_connected() override; virtual void enable_channels(); void on_disconnected() override; @@ -134,7 +135,7 @@ class USBUartTypeCP210X : public USBUartTypeCdcAcm { USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {} protected: - std::vector parse_descriptors_(usb_device_handle_t dev_hdl) override; + std::vector parse_descriptors(usb_device_handle_t dev_hdl) override; void enable_channels() override; }; class USBUartTypeCH34X : public USBUartTypeCdcAcm { From c760f89e465eb75db711bbac91817d0c39909e74 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sat, 12 Jul 2025 19:00:38 -0400 Subject: [PATCH 22/90] [libretiny] Set lib_compat_mode to soft for libretiny (#9439) --- esphome/components/libretiny/__init__.py | 2 +- platformio.ini | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index d5a5dd3ee3..17d5d46ffd 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -268,7 +268,7 @@ async def component_to_code(config): # disable library compatibility checks cg.add_platformio_option("lib_ldf_mode", "off") - cg.add_platformio_option("lib_compat_mode", "strict") + cg.add_platformio_option("lib_compat_mode", "soft") # include in every file cg.add_platformio_option("build_src_flags", "-include Arduino.h") # dummy version code diff --git a/platformio.ini b/platformio.ini index 7f10f0f51f..54c72eb28d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -212,6 +212,7 @@ build_unflags = extends = common:arduino platform = libretiny@1.9.1 framework = arduino +lib_compat_mode = soft lib_deps = droscy/esp_wireguard@0.4.2 ; wireguard build_flags = From 39e01c42e18007fdb367c28fc0089b5262eab302 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 13 Jul 2025 11:05:14 +1200 Subject: [PATCH 23/90] Bump version to 2025.7.0b2 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 3d6147135b..05731fa4ef 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.7.0b1 +PROJECT_NUMBER = 2025.7.0b2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 94d1379b37..0753ea32d4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.7.0b1" +__version__ = "2025.7.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From cd8e1548bf9b7471e7d6188b4881995ad32dd949 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Sat, 12 Jul 2025 18:22:07 -0700 Subject: [PATCH 24/90] (Maybe?) fix I2S speaker internal DAC mode (#9435) --- esphome/components/i2s_audio/speaker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index bb9f24bf0b..cb7b876a40 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -180,7 +180,7 @@ async def to_code(config): await speaker.register_speaker(var, config) if config[CONF_DAC_TYPE] == "internal": - cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL])) + cg.add(var.set_internal_dac_mode(config[CONF_MODE])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) if use_legacy(): From 6f19808effd1509157bf17c06051e1cbae0c0f54 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Sat, 12 Jul 2025 22:43:32 -0700 Subject: [PATCH 25/90] [lvgl] Post-process size arguments in meter config (#9466) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/widgets/meter.py | 33 ++++++++++++++---------- tests/components/lvgl/lvgl-package.yaml | 10 +++---- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index 840511da69..f836a1eca5 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -29,9 +29,9 @@ from ..defines import ( ) from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import ( - angle, get_end_value, get_start_value, + lv_angle, lv_bool, lv_color, lv_float, @@ -162,7 +162,7 @@ SCALE_SCHEMA = cv.Schema( cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_, cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_, cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360), - cv.Optional(CONF_ROTATION): angle, + cv.Optional(CONF_ROTATION): lv_angle, cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA), } ) @@ -187,7 +187,7 @@ class MeterType(WidgetType): for scale_conf in config.get(CONF_SCALES, ()): rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2 if CONF_ROTATION in scale_conf: - rotation = scale_conf[CONF_ROTATION] // 10 + rotation = await lv_angle.process(scale_conf[CONF_ROTATION]) with LocalVariable( "meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var) ) as meter_var: @@ -205,21 +205,20 @@ class MeterType(WidgetType): var, meter_var, ticks[CONF_COUNT], - ticks[CONF_WIDTH], - ticks[CONF_LENGTH], + await size.process(ticks[CONF_WIDTH]), + await size.process(ticks[CONF_LENGTH]), color, ) if CONF_MAJOR in ticks: major = ticks[CONF_MAJOR] - color = await lv_color.process(major[CONF_COLOR]) lv.meter_set_scale_major_ticks( var, meter_var, major[CONF_STRIDE], - major[CONF_WIDTH], - major[CONF_LENGTH], - color, - major[CONF_LABEL_GAP], + await size.process(major[CONF_WIDTH]), + await size.process(major[CONF_LENGTH]), + await lv_color.process(major[CONF_COLOR]), + await size.process(major[CONF_LABEL_GAP]), ) for indicator in scale_conf.get(CONF_INDICATORS, ()): (t, v) = next(iter(indicator.items())) @@ -233,7 +232,11 @@ class MeterType(WidgetType): lv_assign( ivar, lv_expr.meter_add_needle_line( - var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + var, + meter_var, + await size.process(v[CONF_WIDTH]), + color, + await size.process(v[CONF_R_MOD]), ), ) if t == CONF_ARC: @@ -241,7 +244,11 @@ class MeterType(WidgetType): lv_assign( ivar, lv_expr.meter_add_arc( - var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + var, + meter_var, + await size.process(v[CONF_WIDTH]), + color, + await size.process(v[CONF_R_MOD]), ), ) if t == CONF_TICK_STYLE: @@ -257,7 +264,7 @@ class MeterType(WidgetType): color_start, color_end, v[CONF_LOCAL], - v[CONF_WIDTH], + size.process(v[CONF_WIDTH]), ), ) if t == CONF_IMAGE: diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 2edc62b6a1..fbcd2a3fba 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -919,21 +919,21 @@ lvgl: text_color: 0xFFFFFF scales: - ticks: - width: 1 + width: !lambda return 1; count: 61 - length: 20 + length: 20% color: 0xFFFFFF range_from: 0 range_to: 60 angle_range: 360 - rotation: 270 + rotation: !lambda return 2700; indicators: - line: opa: 50% id: minute_hand color: 0xFF0000 - r_mod: -1 - width: 3 + r_mod: !lambda return -1; + width: !lambda return 3; - angle_range: 330 rotation: 300 From 84956b6dc59ac8542df3f753bac2063901607f10 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 12 Jul 2025 22:09:55 -1000 Subject: [PATCH 26/90] Automatically disable interrupts for ESP8266 GPIO16 binary sensors (#9467) --- .../components/gpio/binary_sensor/__init__.py | 27 +++++++- .../gpio/test_gpio_binary_sensor.py | 69 +++++++++++++++++++ .../gpio/test_gpio_binary_sensor.yaml | 11 +++ .../gpio/test_gpio_binary_sensor_esp8266.yaml | 20 ++++++ .../gpio/test_gpio_binary_sensor_polling.yaml | 12 ++++ 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor.py create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor.yaml create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index 9f50fd779a..867a8efe49 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -1,11 +1,16 @@ +import logging + from esphome import pins import esphome.codegen as cg from esphome.components import binary_sensor import esphome.config_validation as cv -from esphome.const import CONF_PIN +from esphome.const import CONF_ID, CONF_NAME, CONF_NUMBER, CONF_PIN +from esphome.core import CORE from .. import gpio_ns +_LOGGER = logging.getLogger(__name__) + GPIOBinarySensor = gpio_ns.class_( "GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component ) @@ -41,6 +46,22 @@ async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) cg.add(var.set_pin(pin)) - cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT])) - if config[CONF_USE_INTERRUPT]: + # Check for ESP8266 GPIO16 interrupt limitation + # GPIO16 on ESP8266 is a special pin that doesn't support interrupts through + # the Arduino attachInterrupt() function. This is the only known GPIO pin + # across all supported platforms that has this limitation, so we handle it + # here instead of in the platform-specific code. + use_interrupt = config[CONF_USE_INTERRUPT] + if use_interrupt and CORE.is_esp8266 and config[CONF_PIN][CONF_NUMBER] == 16: + _LOGGER.warning( + "GPIO binary_sensor '%s': GPIO16 on ESP8266 doesn't support interrupts. " + "Falling back to polling mode (same as in ESPHome <2025.7). " + "The sensor will work exactly as before, but other pins have better " + "performance with interrupts.", + config.get(CONF_NAME, config[CONF_ID]), + ) + use_interrupt = False + + cg.add(var.set_use_interrupt(use_interrupt)) + if use_interrupt: cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE])) diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor.py b/tests/component_tests/gpio/test_gpio_binary_sensor.py new file mode 100644 index 0000000000..74fa2ab1c1 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor.py @@ -0,0 +1,69 @@ +"""Tests for the GPIO binary sensor component.""" + +from __future__ import annotations + +from collections.abc import Callable +from pathlib import Path + +import pytest + + +def test_gpio_binary_sensor_basic_setup( + generate_main: Callable[[str | Path], str], +) -> None: + """ + When the GPIO binary sensor is set in the yaml file, it should be registered in main + """ + main_cpp = generate_main("tests/component_tests/gpio/test_gpio_binary_sensor.yaml") + + assert "new gpio::GPIOBinarySensor();" in main_cpp + assert "App.register_binary_sensor" in main_cpp + assert "bs_gpio->set_use_interrupt(true);" in main_cpp + assert "bs_gpio->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp + + +def test_gpio_binary_sensor_esp8266_gpio16_disables_interrupt( + generate_main: Callable[[str | Path], str], + caplog: pytest.LogCaptureFixture, +) -> None: + """ + Test that ESP8266 GPIO16 automatically disables interrupt mode with a warning + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml" + ) + + # Check that interrupt is disabled for GPIO16 + assert "bs_gpio16->set_use_interrupt(false);" in main_cpp + + # Check that the warning was logged + assert "GPIO16 on ESP8266 doesn't support interrupts" in caplog.text + assert "Falling back to polling mode" in caplog.text + + +def test_gpio_binary_sensor_esp8266_other_pins_use_interrupt( + generate_main: Callable[[str | Path], str], +) -> None: + """ + Test that ESP8266 pins other than GPIO16 still use interrupt mode + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml" + ) + + # GPIO5 should still use interrupts + assert "bs_gpio5->set_use_interrupt(true);" in main_cpp + assert "bs_gpio5->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp + + +def test_gpio_binary_sensor_explicit_polling_mode( + generate_main: Callable[[str | Path], str], +) -> None: + """ + Test that explicitly setting use_interrupt: false works + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml" + ) + + assert "bs_polling->set_use_interrupt(false);" in main_cpp diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor.yaml new file mode 100644 index 0000000000..e258fe0cb4 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor.yaml @@ -0,0 +1,11 @@ +esphome: + name: test + +esp32: + board: esp32dev + +binary_sensor: + - platform: gpio + pin: 5 + name: "Test GPIO Binary Sensor" + id: bs_gpio diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml new file mode 100644 index 0000000000..aec26fe572 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml @@ -0,0 +1,20 @@ +esphome: + name: test + +esp8266: + board: d1_mini + +binary_sensor: + - platform: gpio + pin: + number: 16 + mode: INPUT_PULLDOWN_16 + name: "GPIO16 Touch Sensor" + id: bs_gpio16 + + - platform: gpio + pin: + number: 5 + mode: INPUT_PULLUP + name: "GPIO5 Button" + id: bs_gpio5 diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml new file mode 100644 index 0000000000..7c53e09265 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml @@ -0,0 +1,12 @@ +esphome: + name: test + +esp32: + board: esp32dev + +binary_sensor: + - platform: gpio + pin: 5 + name: "Polling Mode Sensor" + id: bs_polling + use_interrupt: false From 9451781915fd08a5a77351d66d6155105f8d1737 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Jul 2025 21:53:04 +0000 Subject: [PATCH 27/90] Bump aioesphomeapi from 34.2.1 to 35.0.1 (#9474) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ea264a8ac4..8829208f30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile esptool==4.9.0 click==8.1.7 esphome-dashboard==20250514.0 -aioesphomeapi==34.2.1 +aioesphomeapi==35.0.1 zeroconf==0.147.0 puremagic==1.30 ruamel.yaml==0.18.14 # dashboard_import From 9e002cd7a3f1c6e59cf6de73d45b416f4593e6e7 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Sun, 13 Jul 2025 23:58:52 +0200 Subject: [PATCH 28/90] [substitutions] Fix #7189 (#9469) --- esphome/components/substitutions/__init__.py | 7 +++++-- .../fixtures/substitutions/00-simple_var.approved.yaml | 2 ++ .../fixtures/substitutions/00-simple_var.input.yaml | 2 ++ .../fixtures/substitutions/03-closures.approved.yaml | 1 + .../fixtures/substitutions/closures_package.yaml | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 5c346ea616..e494529b9e 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -146,8 +146,11 @@ def _substitute_item(substitutions, item, path, jinja, ignore_missing): if sub is not None: item[k] = sub for old, new in replace_keys: - item[new] = merge_config(item.get(old), item.get(new)) - del item[old] + if str(new) == str(old): + item[new] = item[old] + else: + item[new] = merge_config(item.get(old), item.get(new)) + del item[old] elif isinstance(item, str): sub = _expand_substitutions(substitutions, item, path, jinja, ignore_missing) if isinstance(sub, JinjaStr) or sub != item: diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml index c031399c37..f5d2f8aa20 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml @@ -17,3 +17,5 @@ test_list: - ${undefined_var} - $undefined_var - ${ undefined_var } + - key1: 1 + key2: 2 diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml index 88a4ffb991..5717433c7e 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml @@ -19,3 +19,5 @@ test_list: - ${undefined_var} - $undefined_var - ${ undefined_var } + - key${var1}: 1 + key${var2}: 2 diff --git a/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml b/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml index c8f7d9976c..dad3be8aa7 100644 --- a/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml @@ -6,6 +6,7 @@ package_result: root file - Double substitution also works; the value of var7 is 79, where A is a package var + - key79: Key should substitute to key79 local_results: - The value of B is 5 - 'You will see, however, that diff --git a/tests/unit_tests/fixtures/substitutions/closures_package.yaml b/tests/unit_tests/fixtures/substitutions/closures_package.yaml index e87908814d..4a0be24b93 100644 --- a/tests/unit_tests/fixtures/substitutions/closures_package.yaml +++ b/tests/unit_tests/fixtures/substitutions/closures_package.yaml @@ -1,3 +1,4 @@ package_result: - The value of A*B is ${A * B}, where A is a package var and B is a substitution in the root file - Double substitution also works; the value of var7 is ${var$A}, where A is a package var + - key${var7}: Key should substitute to key79 From 5416cee2c947e29818ebbfa631927e6de84605b5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 12:44:21 -1000 Subject: [PATCH 29/90] Fix pre-commit CI failures by skipping local hooks that require virtual environment (#9476) --- .pre-commit-config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c7955cc88..203808c88b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,13 @@ --- # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks + +ci: + autoupdate_commit_msg: 'pre-commit: autoupdate' + autoupdate_schedule: weekly + # Skip hooks that have issues in pre-commit CI environment + skip: [pylint, clang-tidy-hash, yamllint] + repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. From b21c76a6c6656b4b5121daf15771c5926d76289d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 13:04:14 -1000 Subject: [PATCH 30/90] Fix clang-tidy skipping when Python linters are skipped (#9463) --- .github/workflows/ci.yml | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 503a50c5c2..c043c25936 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -345,20 +345,31 @@ jobs: run: script/ci-suggest-changes if: always() + clang-tidy-deps: + name: Clang-tidy dependencies + runs-on: ubuntu-24.04 + needs: + - common + - ci-custom + - clang-format + - pytest + - determine-jobs + if: | + always() && + needs.determine-jobs.outputs.clang-tidy == 'true' + steps: + - run: echo "All clang-tidy dependencies ready" + clang-tidy: name: ${{ matrix.name }} runs-on: ubuntu-24.04 needs: - - common - - ruff - - ci-custom - - clang-format - - flake8 - - pylint - - pytest - - pyupgrade + - clang-tidy-deps - determine-jobs - if: needs.determine-jobs.outputs.clang-tidy == 'true' + if: | + always() && + needs.determine-jobs.outputs.clang-tidy == 'true' && + needs.clang-tidy-deps.result == 'success' env: GH_TOKEN: ${{ github.token }} strategy: @@ -575,6 +586,7 @@ jobs: - pytest - integration-tests - pyupgrade + - clang-tidy-deps - clang-tidy - determine-jobs - test-build-components From fc337aef6941840a8da8ff66905999e755dd3abb Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:47:52 +1000 Subject: [PATCH 31/90] [esp_ldo] Component schema; default priority (#9479) --- esphome/components/esp_ldo/__init__.py | 18 ++++++++++-------- esphome/components/esp_ldo/esp_ldo.h | 3 +++ .../components/esp_ldo/test.esp32-p4-idf.yaml | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp_ldo/__init__.py b/esphome/components/esp_ldo/__init__.py index ce24028083..38e684c537 100644 --- a/esphome/components/esp_ldo/__init__.py +++ b/esphome/components/esp_ldo/__init__.py @@ -20,14 +20,16 @@ adjusted_ids = set() CONFIG_SCHEMA = cv.All( cv.ensure_list( - { - cv.GenerateID(): cv.declare_id(EspLdo), - cv.Required(CONF_VOLTAGE): cv.All( - cv.voltage, cv.float_range(min=0.5, max=2.7) - ), - cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), - cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, - } + cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(EspLdo), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, cv.float_range(min=0.5, max=2.7) + ), + cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), + cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, + } + ) ), cv.only_with_esp_idf, only_on_variant(supported=[VARIANT_ESP32P4]), diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h index fb5abf7a3a..bafa32db6b 100644 --- a/esphome/components/esp_ldo/esp_ldo.h +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -17,6 +17,9 @@ class EspLdo : public Component { void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; } void set_voltage(float voltage) { this->voltage_ = voltage; } void adjust_voltage(float voltage); + float get_setup_priority() const override { + return setup_priority::BUS; // LDO setup should be done early + } protected: int channel_; diff --git a/tests/components/esp_ldo/test.esp32-p4-idf.yaml b/tests/components/esp_ldo/test.esp32-p4-idf.yaml index 0e91aaf082..38bd6ac411 100644 --- a/tests/components/esp_ldo/test.esp32-p4-idf.yaml +++ b/tests/components/esp_ldo/test.esp32-p4-idf.yaml @@ -6,6 +6,7 @@ esp_ldo: - id: ldo_4 channel: 4 voltage: 2.0V + setup_priority: 900 esphome: on_boot: From 02d1894a9feaa955c2121e2b2db20d3481873db5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 14:32:16 -1000 Subject: [PATCH 32/90] Refactor format_hex_pretty functions to eliminate code duplication (#9480) --- esphome/core/helpers.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index b46077af02..e84f5a7317 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -258,7 +258,9 @@ std::string format_hex(const uint8_t *data, size_t length) { std::string format_hex(const std::vector &data) { return format_hex(data.data(), data.size()); } static char format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; } -std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length) { + +// Shared implementation for uint8_t and string hex formatting +static std::string format_hex_pretty_uint8(const uint8_t *data, size_t length, char separator, bool show_length) { if (data == nullptr || length == 0) return ""; std::string ret; @@ -274,6 +276,10 @@ std::string format_hex_pretty(const uint8_t *data, size_t length, char separator return ret + " (" + std::to_string(length) + ")"; return ret; } + +std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length) { + return format_hex_pretty_uint8(data, length, separator, show_length); +} std::string format_hex_pretty(const std::vector &data, char separator, bool show_length) { return format_hex_pretty(data.data(), data.size(), separator, show_length); } @@ -300,20 +306,7 @@ std::string format_hex_pretty(const std::vector &data, char separator, return format_hex_pretty(data.data(), data.size(), separator, show_length); } std::string format_hex_pretty(const std::string &data, char separator, bool show_length) { - if (data.empty()) - return ""; - std::string ret; - uint8_t multiple = separator ? 3 : 2; // 3 if separator is not \0, 2 otherwise - ret.resize(multiple * data.length() - (separator ? 1 : 0)); - for (size_t i = 0; i < data.length(); i++) { - ret[multiple * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4); - ret[multiple * i + 1] = format_hex_pretty_char(data[i] & 0x0F); - if (separator && i != data.length() - 1) - ret[multiple * i + 2] = separator; - } - if (show_length && data.length() > 4) - return ret + " (" + std::to_string(data.length()) + ")"; - return ret; + return format_hex_pretty_uint8(reinterpret_cast(data.data()), data.length(), separator, show_length); } std::string format_bin(const uint8_t *data, size_t length) { From f5c8595a4633fbcae9fb08321fa659fde429527b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 14:41:49 -1000 Subject: [PATCH 33/90] Follow logging best practices by removing redundant component prefix (#9481) --- esphome/core/component.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 9d863e56cd..b360e1d20b 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -138,7 +138,7 @@ void Component::call_dump_config() { } } } - ESP_LOGE(TAG, " Component %s is marked FAILED: %s", this->get_component_source(), error_msg); + ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(), error_msg); } } @@ -191,7 +191,7 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) { return false; } void Component::mark_failed() { - ESP_LOGE(TAG, "Component %s was marked as failed", this->get_component_source()); + 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->status_set_error(); @@ -229,7 +229,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, "Component %s is being reset to construction state", this->get_component_source()); + 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; // Clear error status when resetting @@ -275,14 +275,14 @@ void Component::status_set_warning(const char *message) { return; this->component_state_ |= STATUS_LED_WARNING; App.app_state_ |= STATUS_LED_WARNING; - ESP_LOGW(TAG, "Component %s set Warning flag: %s", this->get_component_source(), message); + ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message); } void Component::status_set_error(const char *message) { if ((this->component_state_ & STATUS_LED_ERROR) != 0) return; this->component_state_ |= STATUS_LED_ERROR; App.app_state_ |= STATUS_LED_ERROR; - ESP_LOGE(TAG, "Component %s set Error flag: %s", this->get_component_source(), message); + ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message); if (strcmp(message, "unspecified") != 0) { // Lazy allocate the error messages vector if needed if (!component_error_messages) { @@ -303,13 +303,13 @@ void Component::status_clear_warning() { if ((this->component_state_ & STATUS_LED_WARNING) == 0) return; this->component_state_ &= ~STATUS_LED_WARNING; - ESP_LOGW(TAG, "Component %s cleared Warning flag", this->get_component_source()); + ESP_LOGW(TAG, "%s cleared Warning flag", this->get_component_source()); } void Component::status_clear_error() { if ((this->component_state_ & STATUS_LED_ERROR) == 0) return; this->component_state_ &= ~STATUS_LED_ERROR; - ESP_LOGE(TAG, "Component %s cleared Error flag", this->get_component_source()); + ESP_LOGE(TAG, "%s cleared Error flag", this->get_component_source()); } void Component::status_momentary_warning(const std::string &name, uint32_t length) { this->status_set_warning(); @@ -403,7 +403,7 @@ uint32_t WarnIfComponentBlockingGuard::finish() { } if (should_warn) { const char *src = component_ == nullptr ? "" : component_->get_component_source(); - ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); + ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); ESP_LOGW(TAG, "Components should block for at most 30 ms"); } From d31b8ad2e2cdd63884d4f1a7bc42c3677e1eedc1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 14:58:07 -1000 Subject: [PATCH 34/90] Fix dormant bug in RAMAllocator::reallocate() manual_size calculation (#9482) --- esphome/core/helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 58f162ff9d..c3b404ae60 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -783,7 +783,7 @@ template class RAMAllocator { T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); } T *reallocate(T *p, size_t n, size_t manual_size) { - size_t size = n * sizeof(T); + size_t size = n * manual_size; T *ptr = nullptr; #ifdef USE_ESP32 if (this->flags_ & Flags::ALLOC_EXTERNAL) { From 097aac2183641c087f161b2b34648d26fbc497d2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 11 Jul 2025 22:33:36 -0500 Subject: [PATCH 35/90] [ld2420] Memory optimization, code clean-up (#9426) --- .../binary_sensor/ld2420_binary_sensor.cpp | 4 +- .../ld2420/button/reconfig_buttons.cpp | 2 +- esphome/components/ld2420/ld2420.cpp | 123 ++++++++++-------- esphome/components/ld2420/ld2420.h | 20 ++- .../ld2420/number/gate_config_number.cpp | 2 +- .../ld2420/select/operating_mode_select.cpp | 2 +- .../ld2420/sensor/ld2420_sensor.cpp | 4 +- .../ld2420/text_sensor/text_sensor.cpp | 4 +- 8 files changed, 84 insertions(+), 77 deletions(-) diff --git a/esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp b/esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp index c6ea0a348b..d8632e9c19 100644 --- a/esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp +++ b/esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp @@ -5,10 +5,10 @@ namespace esphome { namespace ld2420 { -static const char *const TAG = "LD2420.binary_sensor"; +static const char *const TAG = "ld2420.binary_sensor"; void LD2420BinarySensor::dump_config() { - ESP_LOGCONFIG(TAG, "LD2420 BinarySensor:"); + ESP_LOGCONFIG(TAG, "Binary Sensor:"); LOG_BINARY_SENSOR(" ", "Presence", this->presence_bsensor_); } diff --git a/esphome/components/ld2420/button/reconfig_buttons.cpp b/esphome/components/ld2420/button/reconfig_buttons.cpp index 3537c1d64a..fb8ec2b5a6 100644 --- a/esphome/components/ld2420/button/reconfig_buttons.cpp +++ b/esphome/components/ld2420/button/reconfig_buttons.cpp @@ -2,7 +2,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -static const char *const TAG = "LD2420.button"; +static const char *const TAG = "ld2420.button"; namespace esphome { namespace ld2420 { diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index 8a7d7de23b..0baff368c8 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -137,7 +137,7 @@ static const std::string OP_SIMPLE_MODE_STRING = "Simple"; // Memory-efficient lookup tables struct StringToUint8 { const char *str; - uint8_t value; + const uint8_t value; }; static constexpr StringToUint8 OP_MODE_BY_STR[] = { @@ -155,8 +155,9 @@ static constexpr const char *ERR_MESSAGE[] = { // Helper function for lookups template uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) { for (const auto &entry : arr) { - if (str == entry.str) + if (str == entry.str) { return entry.value; + } } return 0xFF; // Not found } @@ -326,15 +327,8 @@ void LD2420Component::revert_config_action() { void LD2420Component::loop() { // If there is a active send command do not process it here, the send command call will handle it. - if (!this->get_cmd_active_()) { - if (!this->available()) - return; - static uint8_t buffer[2048]; - static uint8_t rx_data; - while (this->available()) { - rx_data = this->read(); - this->readline_(rx_data, buffer, sizeof(buffer)); - } + while (!this->cmd_active_ && this->available()) { + this->readline_(this->read(), this->buffer_data_, MAX_LINE_LENGTH); } } @@ -365,8 +359,9 @@ void LD2420Component::auto_calibrate_sensitivity() { // Store average and peak values this->gate_avg[gate] = sum / CALIBRATE_SAMPLES; - if (this->gate_peak[gate] < peak) + if (this->gate_peak[gate] < peak) { this->gate_peak[gate] = peak; + } uint32_t calculated_value = (static_cast(this->gate_peak[gate]) + (move_factor * static_cast(this->gate_peak[gate]))); @@ -403,8 +398,9 @@ void LD2420Component::set_operating_mode(const std::string &state) { } } else { // Set the current data back so we don't have new data that can be applied in error. - if (this->get_calibration_()) + if (this->get_calibration_()) { memcpy(&this->new_config, &this->current_config, sizeof(this->current_config)); + } this->set_calibration_(false); } } else { @@ -414,30 +410,32 @@ void LD2420Component::set_operating_mode(const std::string &state) { } void LD2420Component::readline_(int rx_data, uint8_t *buffer, int len) { - static int pos = 0; - - if (rx_data >= 0) { - if (pos < len - 1) { - buffer[pos++] = rx_data; - buffer[pos] = 0; - } else { - pos = 0; - } - if (pos >= 4) { - if (memcmp(&buffer[pos - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) { - this->set_cmd_active_(false); // Set command state to inactive after responce. - this->handle_ack_data_(buffer, pos); - pos = 0; - } else if ((buffer[pos - 2] == 0x0D && buffer[pos - 1] == 0x0A) && - (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) { - this->handle_simple_mode_(buffer, pos); - pos = 0; - } else if ((memcmp(&buffer[pos - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) && - (this->get_mode_() == CMD_SYSTEM_MODE_ENERGY)) { - this->handle_energy_mode_(buffer, pos); - pos = 0; - } - } + if (rx_data < 0) { + return; // No data available + } + if (this->buffer_pos_ < len - 1) { + buffer[this->buffer_pos_++] = rx_data; + buffer[this->buffer_pos_] = 0; + } else { + // We should never get here, but just in case... + ESP_LOGW(TAG, "Max command length exceeded; ignoring"); + this->buffer_pos_ = 0; + } + if (this->buffer_pos_ < 4) { + return; // Not enough data to process yet + } + if (memcmp(&buffer[this->buffer_pos_ - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) { + this->cmd_active_ = false; // Set command state to inactive after response + this->handle_ack_data_(buffer, this->buffer_pos_); + this->buffer_pos_ = 0; + } else if ((buffer[this->buffer_pos_ - 2] == 0x0D && buffer[this->buffer_pos_ - 1] == 0x0A) && + (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) { + this->handle_simple_mode_(buffer, this->buffer_pos_); + this->buffer_pos_ = 0; + } else if ((memcmp(&buffer[this->buffer_pos_ - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) && + (this->get_mode_() == CMD_SYSTEM_MODE_ENERGY)) { + this->handle_energy_mode_(buffer, this->buffer_pos_); + this->buffer_pos_ = 0; } } @@ -462,8 +460,9 @@ void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) { // Resonable refresh rate for home assistant database size health const int32_t current_millis = App.get_loop_component_start_time(); - if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) + if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) { return; + } this->last_periodic_millis = current_millis; for (auto &listener : this->listeners_) { listener->on_distance(this->get_distance_()); @@ -506,14 +505,16 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) { } } outbuf[index] = '\0'; - if (index > 1) + if (index > 1) { this->set_distance_(strtol(outbuf, &endptr, 10)); + } if (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE) { // Resonable refresh rate for home assistant database size health const int32_t current_millis = App.get_loop_component_start_time(); - if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) + if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) { return; + } this->last_normal_periodic_millis = current_millis; for (auto &listener : this->listeners_) listener->on_distance(this->get_distance_()); @@ -593,11 +594,12 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) { int LD2420Component::send_cmd_from_array(CmdFrameT frame) { uint32_t start_millis = millis(); uint8_t error = 0; - uint8_t ack_buffer[64]; - uint8_t cmd_buffer[64]; + uint8_t ack_buffer[MAX_LINE_LENGTH]; + uint8_t cmd_buffer[MAX_LINE_LENGTH]; this->cmd_reply_.ack = false; - if (frame.command != CMD_RESTART) - this->set_cmd_active_(true); // Restart does not reply, thus no ack state required. + if (frame.command != CMD_RESTART) { + this->cmd_active_ = true; + } // Restart does not reply, thus no ack state required uint8_t retry = 3; while (retry) { frame.length = 0; @@ -619,9 +621,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { memcpy(cmd_buffer + frame.length, &frame.footer, sizeof(frame.footer)); frame.length += sizeof(frame.footer); - for (uint16_t index = 0; index < frame.length; index++) { - this->write_byte(cmd_buffer[index]); - } + this->write_array(cmd_buffer, frame.length); error = 0; if (frame.command == CMD_RESTART) { @@ -630,7 +630,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { while (!this->cmd_reply_.ack) { while (this->available()) { - this->readline_(read(), ack_buffer, sizeof(ack_buffer)); + this->readline_(this->read(), ack_buffer, sizeof(ack_buffer)); } delay_microseconds_safe(1450); // Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT. @@ -641,10 +641,12 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { break; } } - if (this->cmd_reply_.ack) + if (this->cmd_reply_.ack) { retry = 0; - if (this->cmd_reply_.error > 0) + } + if (this->cmd_reply_.error > 0) { this->handle_cmd_error(error); + } } return error; } @@ -764,8 +766,9 @@ void LD2420Component::set_system_mode(uint16_t mode) { cmd_frame.data_length += sizeof(unknown_parm); cmd_frame.footer = CMD_FRAME_FOOTER; ESP_LOGV(TAG, "Sending write system mode command: %2X", cmd_frame.command); - if (this->send_cmd_from_array(cmd_frame) == 0) + if (this->send_cmd_from_array(cmd_frame) == 0) { this->set_mode_(mode); + } } void LD2420Component::get_firmware_version_() { @@ -840,18 +843,24 @@ void LD2420Component::set_gate_threshold(uint8_t gate) { #ifdef USE_NUMBER void LD2420Component::init_gate_config_numbers() { - if (this->gate_timeout_number_ != nullptr) + if (this->gate_timeout_number_ != nullptr) { this->gate_timeout_number_->publish_state(static_cast(this->current_config.timeout)); - if (this->gate_select_number_ != nullptr) + } + if (this->gate_select_number_ != nullptr) { this->gate_select_number_->publish_state(0); - if (this->min_gate_distance_number_ != nullptr) + } + if (this->min_gate_distance_number_ != nullptr) { this->min_gate_distance_number_->publish_state(static_cast(this->current_config.min_gate)); - if (this->max_gate_distance_number_ != nullptr) + } + if (this->max_gate_distance_number_ != nullptr) { this->max_gate_distance_number_->publish_state(static_cast(this->current_config.max_gate)); - if (this->gate_move_sensitivity_factor_number_ != nullptr) + } + if (this->gate_move_sensitivity_factor_number_ != nullptr) { this->gate_move_sensitivity_factor_number_->publish_state(this->gate_move_sensitivity_factor); - if (this->gate_still_sensitivity_factor_number_ != nullptr) + } + if (this->gate_still_sensitivity_factor_number_ != nullptr) { this->gate_still_sensitivity_factor_number_->publish_state(this->gate_still_sensitivity_factor); + } for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) { if (this->gate_still_threshold_numbers_[gate] != nullptr) { this->gate_still_threshold_numbers_[gate]->publish_state( diff --git a/esphome/components/ld2420/ld2420.h b/esphome/components/ld2420/ld2420.h index d574a25c89..812c408cfd 100644 --- a/esphome/components/ld2420/ld2420.h +++ b/esphome/components/ld2420/ld2420.h @@ -20,8 +20,9 @@ namespace esphome { namespace ld2420 { -static const uint8_t TOTAL_GATES = 16; static const uint8_t CALIBRATE_SAMPLES = 64; +static const uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer +static const uint8_t TOTAL_GATES = 16; enum OpMode : uint8_t { OP_NORMAL_MODE = 1, @@ -118,10 +119,10 @@ class LD2420Component : public Component, public uart::UARTDevice { float gate_move_sensitivity_factor{0.5}; float gate_still_sensitivity_factor{0.5}; - int32_t last_periodic_millis = millis(); - int32_t report_periodic_millis = millis(); - int32_t monitor_periodic_millis = millis(); - int32_t last_normal_periodic_millis = millis(); + int32_t last_periodic_millis{0}; + int32_t report_periodic_millis{0}; + int32_t monitor_periodic_millis{0}; + int32_t last_normal_periodic_millis{0}; uint16_t radar_data[TOTAL_GATES][CALIBRATE_SAMPLES]; uint16_t gate_avg[TOTAL_GATES]; uint16_t gate_peak[TOTAL_GATES]; @@ -161,8 +162,6 @@ class LD2420Component : public Component, public uart::UARTDevice { void set_presence_(bool presence) { this->presence_ = presence; }; uint16_t get_distance_() { return this->distance_; }; void set_distance_(uint16_t distance) { this->distance_ = distance; }; - bool get_cmd_active_() { return this->cmd_active_; }; - void set_cmd_active_(bool active) { this->cmd_active_ = active; }; void handle_simple_mode_(const uint8_t *inbuf, int len); void handle_energy_mode_(uint8_t *buffer, int len); void handle_ack_data_(uint8_t *buffer, int len); @@ -181,12 +180,11 @@ class LD2420Component : public Component, public uart::UARTDevice { std::vector gate_move_threshold_numbers_ = std::vector(16); #endif - uint32_t max_distance_gate_; - uint32_t min_distance_gate_; + uint16_t distance_{0}; uint16_t system_mode_; uint16_t gate_energy_[TOTAL_GATES]; - uint16_t distance_{0}; - uint8_t config_checksum_{0}; + uint8_t buffer_pos_{0}; // where to resume processing/populating buffer + uint8_t buffer_data_[MAX_LINE_LENGTH]; char firmware_ver_[8]{"v0.0.0"}; bool cmd_active_{false}; bool presence_{false}; diff --git a/esphome/components/ld2420/number/gate_config_number.cpp b/esphome/components/ld2420/number/gate_config_number.cpp index e5eaafb46d..a373753770 100644 --- a/esphome/components/ld2420/number/gate_config_number.cpp +++ b/esphome/components/ld2420/number/gate_config_number.cpp @@ -2,7 +2,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -static const char *const TAG = "LD2420.number"; +static const char *const TAG = "ld2420.number"; namespace esphome { namespace ld2420 { diff --git a/esphome/components/ld2420/select/operating_mode_select.cpp b/esphome/components/ld2420/select/operating_mode_select.cpp index 1c59f443a5..2d576e7cc6 100644 --- a/esphome/components/ld2420/select/operating_mode_select.cpp +++ b/esphome/components/ld2420/select/operating_mode_select.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ld2420 { -static const char *const TAG = "LD2420.select"; +static const char *const TAG = "ld2420.select"; void LD2420Select::control(const std::string &value) { this->publish_state(value); diff --git a/esphome/components/ld2420/sensor/ld2420_sensor.cpp b/esphome/components/ld2420/sensor/ld2420_sensor.cpp index 97f0c594b7..723604f396 100644 --- a/esphome/components/ld2420/sensor/ld2420_sensor.cpp +++ b/esphome/components/ld2420/sensor/ld2420_sensor.cpp @@ -5,10 +5,10 @@ namespace esphome { namespace ld2420 { -static const char *const TAG = "LD2420.sensor"; +static const char *const TAG = "ld2420.sensor"; void LD2420Sensor::dump_config() { - ESP_LOGCONFIG(TAG, "LD2420 Sensor:"); + ESP_LOGCONFIG(TAG, "Sensor:"); LOG_SENSOR(" ", "Distance", this->distance_sensor_); } diff --git a/esphome/components/ld2420/text_sensor/text_sensor.cpp b/esphome/components/ld2420/text_sensor/text_sensor.cpp index 1dcdcf7d60..73af3b3660 100644 --- a/esphome/components/ld2420/text_sensor/text_sensor.cpp +++ b/esphome/components/ld2420/text_sensor/text_sensor.cpp @@ -5,10 +5,10 @@ namespace esphome { namespace ld2420 { -static const char *const TAG = "LD2420.text_sensor"; +static const char *const TAG = "ld2420.text_sensor"; void LD2420TextSensor::dump_config() { - ESP_LOGCONFIG(TAG, "LD2420 TextSensor:"); + ESP_LOGCONFIG(TAG, "Text Sensor:"); LOG_TEXT_SENSOR(" ", "Firmware", this->fw_version_text_sensor_); } From 9beb4e2cd418a64098a369536cefa086deb2f8c3 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Sat, 12 Jul 2025 18:22:07 -0700 Subject: [PATCH 36/90] (Maybe?) fix I2S speaker internal DAC mode (#9435) --- esphome/components/i2s_audio/speaker/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index bb9f24bf0b..cb7b876a40 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -180,7 +180,7 @@ async def to_code(config): await speaker.register_speaker(var, config) if config[CONF_DAC_TYPE] == "internal": - cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL])) + cg.add(var.set_internal_dac_mode(config[CONF_MODE])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) if use_legacy(): From 8f42bc6aaca742c2cb1dd729174ef84cbd0547f5 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Sat, 12 Jul 2025 22:43:32 -0700 Subject: [PATCH 37/90] [lvgl] Post-process size arguments in meter config (#9466) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/widgets/meter.py | 33 ++++++++++++++---------- tests/components/lvgl/lvgl-package.yaml | 10 +++---- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index 840511da69..f836a1eca5 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -29,9 +29,9 @@ from ..defines import ( ) from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import ( - angle, get_end_value, get_start_value, + lv_angle, lv_bool, lv_color, lv_float, @@ -162,7 +162,7 @@ SCALE_SCHEMA = cv.Schema( cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_, cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_, cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360), - cv.Optional(CONF_ROTATION): angle, + cv.Optional(CONF_ROTATION): lv_angle, cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA), } ) @@ -187,7 +187,7 @@ class MeterType(WidgetType): for scale_conf in config.get(CONF_SCALES, ()): rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2 if CONF_ROTATION in scale_conf: - rotation = scale_conf[CONF_ROTATION] // 10 + rotation = await lv_angle.process(scale_conf[CONF_ROTATION]) with LocalVariable( "meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var) ) as meter_var: @@ -205,21 +205,20 @@ class MeterType(WidgetType): var, meter_var, ticks[CONF_COUNT], - ticks[CONF_WIDTH], - ticks[CONF_LENGTH], + await size.process(ticks[CONF_WIDTH]), + await size.process(ticks[CONF_LENGTH]), color, ) if CONF_MAJOR in ticks: major = ticks[CONF_MAJOR] - color = await lv_color.process(major[CONF_COLOR]) lv.meter_set_scale_major_ticks( var, meter_var, major[CONF_STRIDE], - major[CONF_WIDTH], - major[CONF_LENGTH], - color, - major[CONF_LABEL_GAP], + await size.process(major[CONF_WIDTH]), + await size.process(major[CONF_LENGTH]), + await lv_color.process(major[CONF_COLOR]), + await size.process(major[CONF_LABEL_GAP]), ) for indicator in scale_conf.get(CONF_INDICATORS, ()): (t, v) = next(iter(indicator.items())) @@ -233,7 +232,11 @@ class MeterType(WidgetType): lv_assign( ivar, lv_expr.meter_add_needle_line( - var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + var, + meter_var, + await size.process(v[CONF_WIDTH]), + color, + await size.process(v[CONF_R_MOD]), ), ) if t == CONF_ARC: @@ -241,7 +244,11 @@ class MeterType(WidgetType): lv_assign( ivar, lv_expr.meter_add_arc( - var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + var, + meter_var, + await size.process(v[CONF_WIDTH]), + color, + await size.process(v[CONF_R_MOD]), ), ) if t == CONF_TICK_STYLE: @@ -257,7 +264,7 @@ class MeterType(WidgetType): color_start, color_end, v[CONF_LOCAL], - v[CONF_WIDTH], + size.process(v[CONF_WIDTH]), ), ) if t == CONF_IMAGE: diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 2edc62b6a1..fbcd2a3fba 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -919,21 +919,21 @@ lvgl: text_color: 0xFFFFFF scales: - ticks: - width: 1 + width: !lambda return 1; count: 61 - length: 20 + length: 20% color: 0xFFFFFF range_from: 0 range_to: 60 angle_range: 360 - rotation: 270 + rotation: !lambda return 2700; indicators: - line: opa: 50% id: minute_hand color: 0xFF0000 - r_mod: -1 - width: 3 + r_mod: !lambda return -1; + width: !lambda return 3; - angle_range: 330 rotation: 300 From 77d1d0414d6903a2c77e32e6d30fb607b3bdc2a2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 12 Jul 2025 22:09:55 -1000 Subject: [PATCH 38/90] Automatically disable interrupts for ESP8266 GPIO16 binary sensors (#9467) --- .../components/gpio/binary_sensor/__init__.py | 27 +++++++- .../gpio/test_gpio_binary_sensor.py | 69 +++++++++++++++++++ .../gpio/test_gpio_binary_sensor.yaml | 11 +++ .../gpio/test_gpio_binary_sensor_esp8266.yaml | 20 ++++++ .../gpio/test_gpio_binary_sensor_polling.yaml | 12 ++++ 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor.py create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor.yaml create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml create mode 100644 tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index 9f50fd779a..867a8efe49 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -1,11 +1,16 @@ +import logging + from esphome import pins import esphome.codegen as cg from esphome.components import binary_sensor import esphome.config_validation as cv -from esphome.const import CONF_PIN +from esphome.const import CONF_ID, CONF_NAME, CONF_NUMBER, CONF_PIN +from esphome.core import CORE from .. import gpio_ns +_LOGGER = logging.getLogger(__name__) + GPIOBinarySensor = gpio_ns.class_( "GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component ) @@ -41,6 +46,22 @@ async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) cg.add(var.set_pin(pin)) - cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT])) - if config[CONF_USE_INTERRUPT]: + # Check for ESP8266 GPIO16 interrupt limitation + # GPIO16 on ESP8266 is a special pin that doesn't support interrupts through + # the Arduino attachInterrupt() function. This is the only known GPIO pin + # across all supported platforms that has this limitation, so we handle it + # here instead of in the platform-specific code. + use_interrupt = config[CONF_USE_INTERRUPT] + if use_interrupt and CORE.is_esp8266 and config[CONF_PIN][CONF_NUMBER] == 16: + _LOGGER.warning( + "GPIO binary_sensor '%s': GPIO16 on ESP8266 doesn't support interrupts. " + "Falling back to polling mode (same as in ESPHome <2025.7). " + "The sensor will work exactly as before, but other pins have better " + "performance with interrupts.", + config.get(CONF_NAME, config[CONF_ID]), + ) + use_interrupt = False + + cg.add(var.set_use_interrupt(use_interrupt)) + if use_interrupt: cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE])) diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor.py b/tests/component_tests/gpio/test_gpio_binary_sensor.py new file mode 100644 index 0000000000..74fa2ab1c1 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor.py @@ -0,0 +1,69 @@ +"""Tests for the GPIO binary sensor component.""" + +from __future__ import annotations + +from collections.abc import Callable +from pathlib import Path + +import pytest + + +def test_gpio_binary_sensor_basic_setup( + generate_main: Callable[[str | Path], str], +) -> None: + """ + When the GPIO binary sensor is set in the yaml file, it should be registered in main + """ + main_cpp = generate_main("tests/component_tests/gpio/test_gpio_binary_sensor.yaml") + + assert "new gpio::GPIOBinarySensor();" in main_cpp + assert "App.register_binary_sensor" in main_cpp + assert "bs_gpio->set_use_interrupt(true);" in main_cpp + assert "bs_gpio->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp + + +def test_gpio_binary_sensor_esp8266_gpio16_disables_interrupt( + generate_main: Callable[[str | Path], str], + caplog: pytest.LogCaptureFixture, +) -> None: + """ + Test that ESP8266 GPIO16 automatically disables interrupt mode with a warning + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml" + ) + + # Check that interrupt is disabled for GPIO16 + assert "bs_gpio16->set_use_interrupt(false);" in main_cpp + + # Check that the warning was logged + assert "GPIO16 on ESP8266 doesn't support interrupts" in caplog.text + assert "Falling back to polling mode" in caplog.text + + +def test_gpio_binary_sensor_esp8266_other_pins_use_interrupt( + generate_main: Callable[[str | Path], str], +) -> None: + """ + Test that ESP8266 pins other than GPIO16 still use interrupt mode + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml" + ) + + # GPIO5 should still use interrupts + assert "bs_gpio5->set_use_interrupt(true);" in main_cpp + assert "bs_gpio5->set_interrupt_type(gpio::INTERRUPT_ANY_EDGE);" in main_cpp + + +def test_gpio_binary_sensor_explicit_polling_mode( + generate_main: Callable[[str | Path], str], +) -> None: + """ + Test that explicitly setting use_interrupt: false works + """ + main_cpp = generate_main( + "tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml" + ) + + assert "bs_polling->set_use_interrupt(false);" in main_cpp diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor.yaml new file mode 100644 index 0000000000..e258fe0cb4 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor.yaml @@ -0,0 +1,11 @@ +esphome: + name: test + +esp32: + board: esp32dev + +binary_sensor: + - platform: gpio + pin: 5 + name: "Test GPIO Binary Sensor" + id: bs_gpio diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml new file mode 100644 index 0000000000..aec26fe572 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor_esp8266.yaml @@ -0,0 +1,20 @@ +esphome: + name: test + +esp8266: + board: d1_mini + +binary_sensor: + - platform: gpio + pin: + number: 16 + mode: INPUT_PULLDOWN_16 + name: "GPIO16 Touch Sensor" + id: bs_gpio16 + + - platform: gpio + pin: + number: 5 + mode: INPUT_PULLUP + name: "GPIO5 Button" + id: bs_gpio5 diff --git a/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml b/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml new file mode 100644 index 0000000000..7c53e09265 --- /dev/null +++ b/tests/component_tests/gpio/test_gpio_binary_sensor_polling.yaml @@ -0,0 +1,12 @@ +esphome: + name: test + +esp32: + board: esp32dev + +binary_sensor: + - platform: gpio + pin: 5 + name: "Polling Mode Sensor" + id: bs_polling + use_interrupt: false From c13317f807f320d6d7989462ead61cdc17415131 Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Sun, 13 Jul 2025 23:58:52 +0200 Subject: [PATCH 39/90] [substitutions] Fix #7189 (#9469) --- esphome/components/substitutions/__init__.py | 7 +++++-- .../fixtures/substitutions/00-simple_var.approved.yaml | 2 ++ .../fixtures/substitutions/00-simple_var.input.yaml | 2 ++ .../fixtures/substitutions/03-closures.approved.yaml | 1 + .../fixtures/substitutions/closures_package.yaml | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 5878af43b2..e07c2e3859 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -151,8 +151,11 @@ def _substitute_item(substitutions, item, path, jinja, ignore_missing): if sub is not None: item[k] = sub for old, new in replace_keys: - item[new] = merge_config(item.get(old), item.get(new)) - del item[old] + if str(new) == str(old): + item[new] = item[old] + else: + item[new] = merge_config(item.get(old), item.get(new)) + del item[old] elif isinstance(item, str): sub = _expand_substitutions(substitutions, item, path, jinja, ignore_missing) if isinstance(sub, JinjaStr) or sub != item: diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml index c031399c37..f5d2f8aa20 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.approved.yaml @@ -17,3 +17,5 @@ test_list: - ${undefined_var} - $undefined_var - ${ undefined_var } + - key1: 1 + key2: 2 diff --git a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml index 88a4ffb991..5717433c7e 100644 --- a/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml +++ b/tests/unit_tests/fixtures/substitutions/00-simple_var.input.yaml @@ -19,3 +19,5 @@ test_list: - ${undefined_var} - $undefined_var - ${ undefined_var } + - key${var1}: 1 + key${var2}: 2 diff --git a/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml b/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml index c8f7d9976c..dad3be8aa7 100644 --- a/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml +++ b/tests/unit_tests/fixtures/substitutions/03-closures.approved.yaml @@ -6,6 +6,7 @@ package_result: root file - Double substitution also works; the value of var7 is 79, where A is a package var + - key79: Key should substitute to key79 local_results: - The value of B is 5 - 'You will see, however, that diff --git a/tests/unit_tests/fixtures/substitutions/closures_package.yaml b/tests/unit_tests/fixtures/substitutions/closures_package.yaml index e87908814d..4a0be24b93 100644 --- a/tests/unit_tests/fixtures/substitutions/closures_package.yaml +++ b/tests/unit_tests/fixtures/substitutions/closures_package.yaml @@ -1,3 +1,4 @@ package_result: - The value of A*B is ${A * B}, where A is a package var and B is a substitution in the root file - Double substitution also works; the value of var7 is ${var$A}, where A is a package var + - key${var7}: Key should substitute to key79 From 9207bf97f316c5ad93da2ffa9f267c8c182e40f0 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Jul 2025 09:47:52 +1000 Subject: [PATCH 40/90] [esp_ldo] Component schema; default priority (#9479) --- esphome/components/esp_ldo/__init__.py | 18 ++++++++++-------- esphome/components/esp_ldo/esp_ldo.h | 3 +++ .../components/esp_ldo/test.esp32-p4-idf.yaml | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp_ldo/__init__.py b/esphome/components/esp_ldo/__init__.py index ce24028083..38e684c537 100644 --- a/esphome/components/esp_ldo/__init__.py +++ b/esphome/components/esp_ldo/__init__.py @@ -20,14 +20,16 @@ adjusted_ids = set() CONFIG_SCHEMA = cv.All( cv.ensure_list( - { - cv.GenerateID(): cv.declare_id(EspLdo), - cv.Required(CONF_VOLTAGE): cv.All( - cv.voltage, cv.float_range(min=0.5, max=2.7) - ), - cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), - cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, - } + cv.COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(EspLdo), + cv.Required(CONF_VOLTAGE): cv.All( + cv.voltage, cv.float_range(min=0.5, max=2.7) + ), + cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True), + cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean, + } + ) ), cv.only_with_esp_idf, only_on_variant(supported=[VARIANT_ESP32P4]), diff --git a/esphome/components/esp_ldo/esp_ldo.h b/esphome/components/esp_ldo/esp_ldo.h index fb5abf7a3a..bafa32db6b 100644 --- a/esphome/components/esp_ldo/esp_ldo.h +++ b/esphome/components/esp_ldo/esp_ldo.h @@ -17,6 +17,9 @@ class EspLdo : public Component { void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; } void set_voltage(float voltage) { this->voltage_ = voltage; } void adjust_voltage(float voltage); + float get_setup_priority() const override { + return setup_priority::BUS; // LDO setup should be done early + } protected: int channel_; diff --git a/tests/components/esp_ldo/test.esp32-p4-idf.yaml b/tests/components/esp_ldo/test.esp32-p4-idf.yaml index 0e91aaf082..38bd6ac411 100644 --- a/tests/components/esp_ldo/test.esp32-p4-idf.yaml +++ b/tests/components/esp_ldo/test.esp32-p4-idf.yaml @@ -6,6 +6,7 @@ esp_ldo: - id: ldo_4 channel: 4 voltage: 2.0V + setup_priority: 900 esphome: on_boot: From e43efdaaecb6bf0fb6783b6da7fc20722d55c4ed Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 14:41:49 -1000 Subject: [PATCH 41/90] Follow logging best practices by removing redundant component prefix (#9481) --- esphome/core/component.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 9d863e56cd..b360e1d20b 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -138,7 +138,7 @@ void Component::call_dump_config() { } } } - ESP_LOGE(TAG, " Component %s is marked FAILED: %s", this->get_component_source(), error_msg); + ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(), error_msg); } } @@ -191,7 +191,7 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) { return false; } void Component::mark_failed() { - ESP_LOGE(TAG, "Component %s was marked as failed", this->get_component_source()); + 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->status_set_error(); @@ -229,7 +229,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, "Component %s is being reset to construction state", this->get_component_source()); + 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; // Clear error status when resetting @@ -275,14 +275,14 @@ void Component::status_set_warning(const char *message) { return; this->component_state_ |= STATUS_LED_WARNING; App.app_state_ |= STATUS_LED_WARNING; - ESP_LOGW(TAG, "Component %s set Warning flag: %s", this->get_component_source(), message); + ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message); } void Component::status_set_error(const char *message) { if ((this->component_state_ & STATUS_LED_ERROR) != 0) return; this->component_state_ |= STATUS_LED_ERROR; App.app_state_ |= STATUS_LED_ERROR; - ESP_LOGE(TAG, "Component %s set Error flag: %s", this->get_component_source(), message); + ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message); if (strcmp(message, "unspecified") != 0) { // Lazy allocate the error messages vector if needed if (!component_error_messages) { @@ -303,13 +303,13 @@ void Component::status_clear_warning() { if ((this->component_state_ & STATUS_LED_WARNING) == 0) return; this->component_state_ &= ~STATUS_LED_WARNING; - ESP_LOGW(TAG, "Component %s cleared Warning flag", this->get_component_source()); + ESP_LOGW(TAG, "%s cleared Warning flag", this->get_component_source()); } void Component::status_clear_error() { if ((this->component_state_ & STATUS_LED_ERROR) == 0) return; this->component_state_ &= ~STATUS_LED_ERROR; - ESP_LOGE(TAG, "Component %s cleared Error flag", this->get_component_source()); + ESP_LOGE(TAG, "%s cleared Error flag", this->get_component_source()); } void Component::status_momentary_warning(const std::string &name, uint32_t length) { this->status_set_warning(); @@ -403,7 +403,7 @@ uint32_t WarnIfComponentBlockingGuard::finish() { } if (should_warn) { const char *src = component_ == nullptr ? "" : component_->get_component_source(); - ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); + ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time); ESP_LOGW(TAG, "Components should block for at most 30 ms"); } From 10ca7ed85b5bc90e0f653445f3dcfe88e1c4ccae Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 14:58:07 -1000 Subject: [PATCH 42/90] Fix dormant bug in RAMAllocator::reallocate() manual_size calculation (#9482) --- esphome/core/helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 58f162ff9d..c3b404ae60 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -783,7 +783,7 @@ template class RAMAllocator { T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); } T *reallocate(T *p, size_t n, size_t manual_size) { - size_t size = n * sizeof(T); + size_t size = n * manual_size; T *ptr = nullptr; #ifdef USE_ESP32 if (this->flags_ & Flags::ALLOC_EXTERNAL) { From b4521e1d8c6dad3f8de33a72951609d610d228f2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:08:24 +1200 Subject: [PATCH 43/90] Bump version to 2025.7.0b3 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 05731fa4ef..42af703a94 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.7.0b2 +PROJECT_NUMBER = 2025.7.0b3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 0753ea32d4..de15050d0c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.7.0b2" +__version__ = "2025.7.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 740c0ef9d7efb0fefd953abbccb6baa7cf77c472 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 12:44:21 -1000 Subject: [PATCH 44/90] Fix pre-commit CI failures by skipping local hooks that require virtual environment (#9476) --- .pre-commit-config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 831473c325..cf00f9bd47 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,13 @@ --- # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks + +ci: + autoupdate_commit_msg: 'pre-commit: autoupdate' + autoupdate_schedule: weekly + # Skip hooks that have issues in pre-commit CI environment + skip: [pylint, clang-tidy-hash, yamllint] + repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. From 4153380f99af004e7072f0e293610e4feb4b0304 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:48:41 +1200 Subject: [PATCH 45/90] Remove hook that doesnt exist on beta --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cf00f9bd47..837c71cf38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ ci: autoupdate_commit_msg: 'pre-commit: autoupdate' autoupdate_schedule: weekly # Skip hooks that have issues in pre-commit CI environment - skip: [pylint, clang-tidy-hash, yamllint] + skip: [pylint, yamllint] repos: - repo: https://github.com/astral-sh/ruff-pre-commit From 90f0ebb22b5a8294b295a002748378ba05f16aaf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 14 Jul 2025 14:12:26 +1200 Subject: [PATCH 46/90] Dont autofix PR --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 837c71cf38..83ea2dc823 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,7 @@ ci: autoupdate_commit_msg: 'pre-commit: autoupdate' autoupdate_schedule: weekly + autofix_prs: false # Skip hooks that have issues in pre-commit CI environment skip: [pylint, yamllint] From 873f4125c57dba83b54e6baca19b0bc284412796 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 17:30:34 -1000 Subject: [PATCH 47/90] Fix pre-commit CI issues by switching to lite mode (#9484) --- .github/workflows/ci.yml | 105 +++++++++------------------------------ .pre-commit-config.yaml | 2 +- 2 files changed, 24 insertions(+), 83 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c043c25936..2e2e114fdb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: uses: actions/checkout@v4.2.2 - name: Generate cache-key id: cache-key - run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT + run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v5.6.0 @@ -58,55 +58,9 @@ jobs: python -m venv venv . venv/bin/activate python --version - pip install -r requirements.txt -r requirements_test.txt + pip install -r requirements.txt -r requirements_test.txt pre-commit pip install -e . - ruff: - name: Check ruff - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: needs.determine-jobs.outputs.python-linters == 'true' - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.2.2 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Run Ruff - run: | - . venv/bin/activate - ruff format esphome tests - - name: Suggested changes - run: script/ci-suggest-changes - if: always() - - flake8: - name: Check flake8 - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: needs.determine-jobs.outputs.python-linters == 'true' - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.2.2 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Run flake8 - run: | - . venv/bin/activate - flake8 esphome - - name: Suggested changes - run: script/ci-suggest-changes - if: always() - pylint: name: Check pylint runs-on: ubuntu-24.04 @@ -248,7 +202,6 @@ jobs: outputs: integration-tests: ${{ steps.determine.outputs.integration-tests }} clang-tidy: ${{ steps.determine.outputs.clang-tidy }} - clang-format: ${{ steps.determine.outputs.clang-format }} python-linters: ${{ steps.determine.outputs.python-linters }} changed-components: ${{ steps.determine.outputs.changed-components }} component-test-count: ${{ steps.determine.outputs.component-test-count }} @@ -276,7 +229,6 @@ jobs: # Extract individual fields echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT - echo "clang-format=$(echo "$output" | jq -r '.clang_format')" >> $GITHUB_OUTPUT echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT @@ -317,41 +269,12 @@ jobs: . venv/bin/activate pytest -vv --no-cov --tb=native -n auto tests/integration/ - clang-format: - name: Check clang-format - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: needs.determine-jobs.outputs.clang-format == 'true' - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.2.2 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Install clang-format - run: | - . venv/bin/activate - pip install clang-format -c requirements_dev.txt - - name: Run clang-format - run: | - . venv/bin/activate - script/clang-format -i - git diff-index --quiet HEAD -- - - name: Suggested changes - run: script/ci-suggest-changes - if: always() - clang-tidy-deps: name: Clang-tidy dependencies runs-on: ubuntu-24.04 needs: - common - ci-custom - - clang-format - pytest - determine-jobs if: | @@ -573,15 +496,32 @@ jobs: ./script/test_build_components -e compile -c $component done + pre-commit-ci-lite: + name: pre-commit.ci lite + runs-on: ubuntu-latest + needs: + - common + if: github.event_name == 'pull_request' && github.base_ref != 'beta' && github.base_ref != 'release' + steps: + - name: Check out code from GitHub + uses: actions/checkout@v4.2.2 + - name: Restore Python + uses: ./.github/actions/restore-python + with: + python-version: ${{ env.DEFAULT_PYTHON }} + cache-key: ${{ needs.common.outputs.cache-key }} + - uses: pre-commit/action@v3.0.1 + env: + SKIP: pylint,clang-tidy-hash,yamllint + - uses: pre-commit-ci/lite-action@v1.1.0 + if: always() + ci-status: name: CI Status runs-on: ubuntu-24.04 needs: - common - - ruff - ci-custom - - clang-format - - flake8 - pylint - pytest - integration-tests @@ -592,6 +532,7 @@ jobs: - test-build-components - test-build-components-splitter - test-build-components-split + - pre-commit-ci-lite if: always() steps: - name: Success diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 203808c88b..8db7f21599 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: autoupdate_commit_msg: 'pre-commit: autoupdate' - autoupdate_schedule: weekly + autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit # Skip hooks that have issues in pre-commit CI environment skip: [pylint, clang-tidy-hash, yamllint] From e7d819a6564483a67f1df4a0446e386d2109c8fe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 21:47:52 -1000 Subject: [PATCH 48/90] Suppress spurious volatile and Python syntax warnings during builds (#9488) --- esphome/platformio_api.py | 2 ++ esphome/writer.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 808db03231..e34ac028f8 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -78,6 +78,8 @@ def run_platformio_cli(*args, **kwargs) -> str | int: os.environ.setdefault( "PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path()) ) + # Suppress Python syntax warnings from third-party scripts during compilation + os.environ.setdefault("PYTHONWARNINGS", "ignore::SyntaxWarning") cmd = ["platformio"] + list(args) if not CORE.verbose: diff --git a/esphome/writer.py b/esphome/writer.py index 943dfa78cc..ca9e511c19 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -162,6 +162,9 @@ def get_ini_content(): # Sort to avoid changing build unflags order CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags)) + # Add extra script for C++ flags + CORE.add_platformio_option("extra_scripts", ["pre:cxx_flags.py"]) + content = "[platformio]\n" content += f"description = ESPHome {__version__}\n" @@ -222,6 +225,9 @@ def write_platformio_project(): write_gitignore() write_platformio_ini(content) + # Write extra script for C++ specific flags + write_cxx_flags_script() + DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\ #pragma once @@ -394,3 +400,16 @@ def write_gitignore(): if not os.path.isfile(path): with open(file=path, mode="w", encoding="utf-8") as f: f.write(GITIGNORE_CONTENT) + + +CXX_FLAGS_SCRIPT = """# Auto-generated ESPHome script for C++ specific compiler flags +Import("env") + +# Add C++ specific warning flags +env.Append(CXXFLAGS=["-Wno-volatile"]) +""" + + +def write_cxx_flags_script() -> None: + path = CORE.relative_build_path("cxx_flags.py") + write_file_if_changed(path, CXX_FLAGS_SCRIPT) From f8c45573f3375ea9379e5c6fe1267e6436bc9497 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 09:24:20 -1000 Subject: [PATCH 49/90] Refactor WebServer request handling for improved maintainability (#9470) --- esphome/components/web_server/web_server.cpp | 343 ++++++++----------- 1 file changed, 140 insertions(+), 203 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 8ced5b7e18..2aa6acde0e 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1711,162 +1711,161 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c #endif bool WebServer::canHandle(AsyncWebServerRequest *request) const { - if (request->url() == "/") + const auto &url = request->url(); + const auto method = request->method(); + + // Simple URL checks + if (url == "/") return true; #ifdef USE_ARDUINO - if (request->url() == "/events") { + if (url == "/events") return true; - } #endif #ifdef USE_WEBSERVER_CSS_INCLUDE - if (request->url() == "/0.css") + if (url == "/0.css") return true; #endif #ifdef USE_WEBSERVER_JS_INCLUDE - if (request->url() == "/0.js") + if (url == "/0.js") return true; #endif #ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS - if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) { + if (method == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) return true; - } #endif - // Store the URL to prevent temporary string destruction - // request->url() returns a reference to a String (on Arduino) or std::string (on ESP-IDF) - // UrlMatch stores pointers to the string's data, so we must ensure the string outlives match_url() - const auto &url = request->url(); + // Parse URL for component checks UrlMatch match = match_url(url.c_str(), url.length(), true); if (!match.valid) return false; + + // Common pattern check + bool is_get = method == HTTP_GET; + bool is_post = method == HTTP_POST; + bool is_get_or_post = is_get || is_post; + + if (!is_get_or_post) + return false; + + // GET-only components + if (is_get) { #ifdef USE_SENSOR - if (request->method() == HTTP_GET && match.domain_equals("sensor")) - return true; + if (match.domain_equals("sensor")) + return true; #endif - -#ifdef USE_SWITCH - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("switch")) - return true; -#endif - -#ifdef USE_BUTTON - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("button")) - return true; -#endif - #ifdef USE_BINARY_SENSOR - if (request->method() == HTTP_GET && match.domain_equals("binary_sensor")) - return true; + if (match.domain_equals("binary_sensor")) + return true; #endif - -#ifdef USE_FAN - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("fan")) - return true; -#endif - -#ifdef USE_LIGHT - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("light")) - return true; -#endif - #ifdef USE_TEXT_SENSOR - if (request->method() == HTTP_GET && match.domain_equals("text_sensor")) - return true; + if (match.domain_equals("text_sensor")) + return true; #endif - -#ifdef USE_COVER - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("cover")) - return true; -#endif - -#ifdef USE_NUMBER - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("number")) - return true; -#endif - -#ifdef USE_DATETIME_DATE - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("date")) - return true; -#endif - -#ifdef USE_DATETIME_TIME - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("time")) - return true; -#endif - -#ifdef USE_DATETIME_DATETIME - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("datetime")) - return true; -#endif - -#ifdef USE_TEXT - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("text")) - return true; -#endif - -#ifdef USE_SELECT - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("select")) - return true; -#endif - -#ifdef USE_CLIMATE - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("climate")) - return true; -#endif - -#ifdef USE_LOCK - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("lock")) - return true; -#endif - -#ifdef USE_VALVE - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("valve")) - return true; -#endif - -#ifdef USE_ALARM_CONTROL_PANEL - if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain_equals("alarm_control_panel")) - return true; -#endif - #ifdef USE_EVENT - if (request->method() == HTTP_GET && match.domain_equals("event")) - return true; + if (match.domain_equals("event")) + return true; #endif + } -#ifdef USE_UPDATE - if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain_equals("update")) - return true; + // GET+POST components + if (is_get_or_post) { +#ifdef USE_SWITCH + if (match.domain_equals("switch")) + return true; #endif +#ifdef USE_BUTTON + if (match.domain_equals("button")) + return true; +#endif +#ifdef USE_FAN + if (match.domain_equals("fan")) + return true; +#endif +#ifdef USE_LIGHT + if (match.domain_equals("light")) + return true; +#endif +#ifdef USE_COVER + if (match.domain_equals("cover")) + return true; +#endif +#ifdef USE_NUMBER + if (match.domain_equals("number")) + return true; +#endif +#ifdef USE_DATETIME_DATE + if (match.domain_equals("date")) + return true; +#endif +#ifdef USE_DATETIME_TIME + if (match.domain_equals("time")) + return true; +#endif +#ifdef USE_DATETIME_DATETIME + if (match.domain_equals("datetime")) + return true; +#endif +#ifdef USE_TEXT + if (match.domain_equals("text")) + return true; +#endif +#ifdef USE_SELECT + if (match.domain_equals("select")) + return true; +#endif +#ifdef USE_CLIMATE + if (match.domain_equals("climate")) + return true; +#endif +#ifdef USE_LOCK + if (match.domain_equals("lock")) + return true; +#endif +#ifdef USE_VALVE + if (match.domain_equals("valve")) + return true; +#endif +#ifdef USE_ALARM_CONTROL_PANEL + if (match.domain_equals("alarm_control_panel")) + return true; +#endif +#ifdef USE_UPDATE + if (match.domain_equals("update")) + return true; +#endif + } return false; } void WebServer::handleRequest(AsyncWebServerRequest *request) { - if (request->url() == "/") { + const auto &url = request->url(); + + // Handle static routes first + if (url == "/") { this->handle_index_request(request); return; } #ifdef USE_ARDUINO - if (request->url() == "/events") { + if (url == "/events") { this->events_.add_new_client(this, request); return; } #endif #ifdef USE_WEBSERVER_CSS_INCLUDE - if (request->url() == "/0.css") { + if (url == "/0.css") { this->handle_css_request(request); return; } #endif #ifdef USE_WEBSERVER_JS_INCLUDE - if (request->url() == "/0.js") { + if (url == "/0.js") { this->handle_js_request(request); return; } @@ -1879,147 +1878,85 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { } #endif - // See comment in canHandle() for why we store the URL reference - const auto &url = request->url(); + // Parse URL for component routing UrlMatch match = match_url(url.c_str(), url.length(), false); + // Component routing using minimal code repetition + struct ComponentRoute { + const char *domain; + void (WebServer::*handler)(AsyncWebServerRequest *, const UrlMatch &); + }; + + static const ComponentRoute routes[] = { #ifdef USE_SENSOR - if (match.domain_equals("sensor")) { - this->handle_sensor_request(request, match); - return; - } + {"sensor", &WebServer::handle_sensor_request}, #endif - #ifdef USE_SWITCH - if (match.domain_equals("switch")) { - this->handle_switch_request(request, match); - return; - } + {"switch", &WebServer::handle_switch_request}, #endif - #ifdef USE_BUTTON - if (match.domain_equals("button")) { - this->handle_button_request(request, match); - return; - } + {"button", &WebServer::handle_button_request}, #endif - #ifdef USE_BINARY_SENSOR - if (match.domain_equals("binary_sensor")) { - this->handle_binary_sensor_request(request, match); - return; - } + {"binary_sensor", &WebServer::handle_binary_sensor_request}, #endif - #ifdef USE_FAN - if (match.domain_equals("fan")) { - this->handle_fan_request(request, match); - return; - } + {"fan", &WebServer::handle_fan_request}, #endif - #ifdef USE_LIGHT - if (match.domain_equals("light")) { - this->handle_light_request(request, match); - return; - } + {"light", &WebServer::handle_light_request}, #endif - #ifdef USE_TEXT_SENSOR - if (match.domain_equals("text_sensor")) { - this->handle_text_sensor_request(request, match); - return; - } + {"text_sensor", &WebServer::handle_text_sensor_request}, #endif - #ifdef USE_COVER - if (match.domain_equals("cover")) { - this->handle_cover_request(request, match); - return; - } + {"cover", &WebServer::handle_cover_request}, #endif - #ifdef USE_NUMBER - if (match.domain_equals("number")) { - this->handle_number_request(request, match); - return; - } + {"number", &WebServer::handle_number_request}, #endif - #ifdef USE_DATETIME_DATE - if (match.domain_equals("date")) { - this->handle_date_request(request, match); - return; - } + {"date", &WebServer::handle_date_request}, #endif - #ifdef USE_DATETIME_TIME - if (match.domain_equals("time")) { - this->handle_time_request(request, match); - return; - } + {"time", &WebServer::handle_time_request}, #endif - #ifdef USE_DATETIME_DATETIME - if (match.domain_equals("datetime")) { - this->handle_datetime_request(request, match); - return; - } + {"datetime", &WebServer::handle_datetime_request}, #endif - #ifdef USE_TEXT - if (match.domain_equals("text")) { - this->handle_text_request(request, match); - return; - } + {"text", &WebServer::handle_text_request}, #endif - #ifdef USE_SELECT - if (match.domain_equals("select")) { - this->handle_select_request(request, match); - return; - } + {"select", &WebServer::handle_select_request}, #endif - #ifdef USE_CLIMATE - if (match.domain_equals("climate")) { - this->handle_climate_request(request, match); - return; - } + {"climate", &WebServer::handle_climate_request}, #endif - #ifdef USE_LOCK - if (match.domain_equals("lock")) { - this->handle_lock_request(request, match); - - return; - } + {"lock", &WebServer::handle_lock_request}, #endif - #ifdef USE_VALVE - if (match.domain_equals("valve")) { - this->handle_valve_request(request, match); - return; - } + {"valve", &WebServer::handle_valve_request}, #endif - #ifdef USE_ALARM_CONTROL_PANEL - if (match.domain_equals("alarm_control_panel")) { - this->handle_alarm_control_panel_request(request, match); - - return; - } + {"alarm_control_panel", &WebServer::handle_alarm_control_panel_request}, #endif - #ifdef USE_UPDATE - if (match.domain_equals("update")) { - this->handle_update_request(request, match); - return; - } + {"update", &WebServer::handle_update_request}, #endif + }; + + // Check each route + for (const auto &route : routes) { + if (match.domain_equals(route.domain)) { + (this->*route.handler)(request, match); + return; + } + } // No matching handler found - send 404 - ESP_LOGV(TAG, "Request for unknown URL: %s", request->url().c_str()); + ESP_LOGV(TAG, "Request for unknown URL: %s", url.c_str()); request->send(404, "text/plain", "Not Found"); } From f78e71c86a66fb37d4fcc1420a6247481555b494 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 10:13:24 -1000 Subject: [PATCH 50/90] Fix WebServer routes constant naming convention (#9497) --- esphome/components/web_server/web_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 2aa6acde0e..da5c6b7cd7 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1887,7 +1887,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { void (WebServer::*handler)(AsyncWebServerRequest *, const UrlMatch &); }; - static const ComponentRoute routes[] = { + static const ComponentRoute ROUTES[] = { #ifdef USE_SENSOR {"sensor", &WebServer::handle_sensor_request}, #endif @@ -1948,7 +1948,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { }; // Check each route - for (const auto &route : routes) { + for (const auto &route : ROUTES) { if (match.domain_equals(route.domain)) { (this->*route.handler)(request, match); return; From 619e2d69c03a9252aa7ec80421a3db2075c1b6d7 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 14:20:05 -1000 Subject: [PATCH 51/90] Remove redundant pyupgrade CI job (follow-up to #9484) (#9493) --- .github/workflows/ci.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e2e114fdb..16943861ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,29 +84,6 @@ jobs: run: script/ci-suggest-changes if: always() - pyupgrade: - name: Check pyupgrade - runs-on: ubuntu-24.04 - needs: - - common - - determine-jobs - if: needs.determine-jobs.outputs.python-linters == 'true' - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.2.2 - - name: Restore Python - uses: ./.github/actions/restore-python - with: - python-version: ${{ env.DEFAULT_PYTHON }} - cache-key: ${{ needs.common.outputs.cache-key }} - - name: Run pyupgrade - run: | - . venv/bin/activate - pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f` - - name: Suggested changes - run: script/ci-suggest-changes - if: always() - ci-custom: name: Run script/ci-custom runs-on: ubuntu-24.04 @@ -525,7 +502,6 @@ jobs: - pylint - pytest - integration-tests - - pyupgrade - clang-tidy-deps - clang-tidy - determine-jobs From b2a8b0a22fd548c228d3f71c07b52060f8a6478f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 14:25:18 -1000 Subject: [PATCH 52/90] Add pre-commit hooks to fix common formatting issues causing CI failures (#9494) --- .clang-tidy.hash | 2 +- .coveragerc | 2 +- .github/workflows/ci-clang-tidy-hash.yml | 1 - .pre-commit-config.yaml | 5 ++++- script/clang_tidy_hash.py | 3 ++- script/test_build_components | 4 ++-- tests/components/animation/.gitattributes | 1 - tests/components/animation/test.esp32-ard.yaml | 1 - tests/components/ble_presence/common.yaml | 1 - tests/components/color/common.yaml | 1 - tests/components/const/common.yaml | 1 - tests/components/cst816/common.yaml | 1 - tests/components/debug/common.yaml | 1 - tests/components/ens160_spi/common.yaml | 1 - tests/components/esp32/test.esp32-idf.yaml | 1 - tests/components/esphome/test.esp32-ard.yaml | 1 - tests/components/esphome/test.esp32-c3-ard.yaml | 1 - tests/components/font/.gitattributes | 1 - tests/components/lvgl/.gitattributes | 1 - tests/components/lvgl/test.esp32-ard.yaml | 1 - tests/components/max17043/test.esp32-ard.yaml | 1 - tests/components/mipi_spi/common.yaml | 1 - tests/components/nextion/test.rp2040-ard.yaml | 1 - tests/components/online_image/test.esp32-idf.yaml | 1 - tests/components/openthread/test.esp32-c6-idf.yaml | 1 - tests/components/packages/test.esp32-ard.yaml | 1 - tests/components/packages/test.esp32-idf.yaml | 1 - tests/components/qspi_dbi/common.yaml | 1 - tests/components/spi/test.esp32-p4-idf.yaml | 1 - tests/components/spi/test.esp32-s3-idf.yaml | 1 - tests/components/udp/common.yaml | 1 - tests/components/web_server/test_ota.esp32-idf.yaml | 1 - tests/components/wk2132_i2c/test.esp32-ard.yaml | 1 - tests/integration/fixtures/areas_and_devices.yaml | 1 - tests/integration/fixtures/device_id_in_state.yaml | 1 - tests/integration/fixtures/large_message_batching.yaml | 1 - tests/script/test_clang_tidy_hash.py | 2 +- tests/unit_tests/fixtures/substitutions/.gitignore | 2 +- 38 files changed, 12 insertions(+), 39 deletions(-) diff --git a/.clang-tidy.hash b/.clang-tidy.hash index 30c52f5baa..d36ae1f164 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a \ No newline at end of file +a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a diff --git a/.coveragerc b/.coveragerc index 12e48ec395..f23592be24 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,4 @@ [run] -omit = +omit = esphome/components/* tests/integration/* diff --git a/.github/workflows/ci-clang-tidy-hash.yml b/.github/workflows/ci-clang-tidy-hash.yml index 4e89da267c..1c7a62e40b 100644 --- a/.github/workflows/ci-clang-tidy-hash.yml +++ b/.github/workflows/ci-clang-tidy-hash.yml @@ -73,4 +73,3 @@ jobs: }); } } - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8db7f21599..5a522d9d36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,13 +27,15 @@ repos: - pydocstyle==5.1.1 files: ^(esphome|tests)/.+\.py$ - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v5.0.0 hooks: - id: no-commit-to-branch args: - --branch=dev - --branch=release - --branch=beta + - id: end-of-file-fixer + - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade rev: v3.20.0 hooks: @@ -43,6 +45,7 @@ repos: rev: v1.37.1 hooks: - id: yamllint + exclude: ^(\.clang-format|\.clang-tidy)$ - repo: https://github.com/pre-commit/mirrors-clang-format rev: v13.0.1 hooks: diff --git a/script/clang_tidy_hash.py b/script/clang_tidy_hash.py index 86f4c4e158..8cc135f5d0 100755 --- a/script/clang_tidy_hash.py +++ b/script/clang_tidy_hash.py @@ -126,7 +126,8 @@ def write_file_content(path: Path, content: str) -> None: def write_hash(hash_value: str) -> None: """Write hash to file""" hash_file = Path(__file__).parent.parent / ".clang-tidy.hash" - write_file_content(hash_file, hash_value) + # Strip any trailing newlines to ensure consistent formatting + write_file_content(hash_file, hash_value.strip() + "\n") def main() -> None: diff --git a/script/test_build_components b/script/test_build_components index 83ab947fc1..3796280176 100755 --- a/script/test_build_components +++ b/script/test_build_components @@ -72,7 +72,7 @@ for f in ./tests/components/$target_component/*.*.yaml; do if [ "$target_platform" = "all" ] || [ $file_name_parts = 2 ]; then # Test has *not* defined a specific target platform. Need to run tests for all possible target platforms. - + for target_platform_file in ./tests/test_build_components/build_components_base.*.yaml; do IFS='/' read -r -a folder_name <<< "$target_platform_file" IFS='.' read -r -a file_name <<< "${folder_name[3]}" @@ -83,7 +83,7 @@ for f in ./tests/components/$target_component/*.*.yaml; do else # Test has defined a specific target platform. - + # Validate we have a base test yaml for selected platform. # The target_platform is sourced from the following location. # 1. `./tests/test_build_components/build_components_base.[target_platform].yaml` diff --git a/tests/components/animation/.gitattributes b/tests/components/animation/.gitattributes index ff9fc6f1f1..766b86f19e 100644 --- a/tests/components/animation/.gitattributes +++ b/tests/components/animation/.gitattributes @@ -1,4 +1,3 @@ *.apng -text *.webp -text *.gif -text - diff --git a/tests/components/animation/test.esp32-ard.yaml b/tests/components/animation/test.esp32-ard.yaml index 5d330900df..7d9fe45bff 100644 --- a/tests/components/animation/test.esp32-ard.yaml +++ b/tests/components/animation/test.esp32-ard.yaml @@ -15,4 +15,3 @@ display: packages: animation: !include common.yaml - diff --git a/tests/components/ble_presence/common.yaml b/tests/components/ble_presence/common.yaml index 6e5173eed8..2ba6aa0754 100644 --- a/tests/components/ble_presence/common.yaml +++ b/tests/components/ble_presence/common.yaml @@ -21,4 +21,3 @@ binary_sensor: - platform: ble_presence irk: 1234567890abcdef1234567890abcdef name: "ESP32 BLE Tracker with Identity Resolving Key" - diff --git a/tests/components/color/common.yaml b/tests/components/color/common.yaml index 88524e6a5f..69a77326f7 100644 --- a/tests/components/color/common.yaml +++ b/tests/components/color/common.yaml @@ -17,4 +17,3 @@ color: hex: 008000 - id: cps_blue hex: 000080 - diff --git a/tests/components/const/common.yaml b/tests/components/const/common.yaml index 655af304af..f4b15f2b90 100644 --- a/tests/components/const/common.yaml +++ b/tests/components/const/common.yaml @@ -41,4 +41,3 @@ display: - delay 120ms - [0x29] - delay 20ms - diff --git a/tests/components/cst816/common.yaml b/tests/components/cst816/common.yaml index 9750de15db..9400e4eef0 100644 --- a/tests/components/cst816/common.yaml +++ b/tests/components/cst816/common.yaml @@ -42,4 +42,3 @@ binary_sensor: x_max: 480 y_min: 320 y_max: 360 - diff --git a/tests/components/debug/common.yaml b/tests/components/debug/common.yaml index a9d74e6865..d9a61f8df0 100644 --- a/tests/components/debug/common.yaml +++ b/tests/components/debug/common.yaml @@ -15,4 +15,3 @@ sensor: name: "Loop Time" cpu_frequency: name: "CPU Frequency" - diff --git a/tests/components/ens160_spi/common.yaml b/tests/components/ens160_spi/common.yaml index 7250ead228..c8b663272f 100644 --- a/tests/components/ens160_spi/common.yaml +++ b/tests/components/ens160_spi/common.yaml @@ -14,4 +14,3 @@ sensor: name: "ENS160 Total Volatile Organic Compounds" aqi: name: "ENS160 Air Quality Index" - diff --git a/tests/components/esp32/test.esp32-idf.yaml b/tests/components/esp32/test.esp32-idf.yaml index 582b2c22ce..ccf0b7cbd5 100644 --- a/tests/components/esp32/test.esp32-idf.yaml +++ b/tests/components/esp32/test.esp32-idf.yaml @@ -9,4 +9,3 @@ esp32: wifi: ssid: MySSID password: password1 - diff --git a/tests/components/esphome/test.esp32-ard.yaml b/tests/components/esphome/test.esp32-ard.yaml index a991f7e15a..dade44d145 100644 --- a/tests/components/esphome/test.esp32-ard.yaml +++ b/tests/components/esphome/test.esp32-ard.yaml @@ -1,2 +1 @@ <<: !include common.yaml - diff --git a/tests/components/esphome/test.esp32-c3-ard.yaml b/tests/components/esphome/test.esp32-c3-ard.yaml index a991f7e15a..dade44d145 100644 --- a/tests/components/esphome/test.esp32-c3-ard.yaml +++ b/tests/components/esphome/test.esp32-c3-ard.yaml @@ -1,2 +1 @@ <<: !include common.yaml - diff --git a/tests/components/font/.gitattributes b/tests/components/font/.gitattributes index 18d9a389e8..63ab00e9f2 100644 --- a/tests/components/font/.gitattributes +++ b/tests/components/font/.gitattributes @@ -1,2 +1 @@ *.pcf -text - diff --git a/tests/components/lvgl/.gitattributes b/tests/components/lvgl/.gitattributes index 75e7a44254..9d74867fcf 100644 --- a/tests/components/lvgl/.gitattributes +++ b/tests/components/lvgl/.gitattributes @@ -1,2 +1 @@ *.ttf -text - diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml index 5b09147de7..f85bedbde6 100644 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -56,4 +56,3 @@ lvgl: packages: lvgl: !include lvgl-package.yaml xvgl: !include common.yaml - diff --git a/tests/components/max17043/test.esp32-ard.yaml b/tests/components/max17043/test.esp32-ard.yaml index c84e0a5c2e..c6615f51cd 100644 --- a/tests/components/max17043/test.esp32-ard.yaml +++ b/tests/components/max17043/test.esp32-ard.yaml @@ -3,4 +3,3 @@ substitutions: scl_pin: GPIO22 <<: !include common.yaml - diff --git a/tests/components/mipi_spi/common.yaml b/tests/components/mipi_spi/common.yaml index e4b1e2b30c..2c84489ec7 100644 --- a/tests/components/mipi_spi/common.yaml +++ b/tests/components/mipi_spi/common.yaml @@ -35,4 +35,3 @@ display: allow_other_uses: true - number: ${enable_pin} bus_mode: single - diff --git a/tests/components/nextion/test.rp2040-ard.yaml b/tests/components/nextion/test.rp2040-ard.yaml index 20347c6eff..44534b97a8 100644 --- a/tests/components/nextion/test.rp2040-ard.yaml +++ b/tests/components/nextion/test.rp2040-ard.yaml @@ -4,4 +4,3 @@ substitutions: packages: base: !include common.yaml - diff --git a/tests/components/online_image/test.esp32-idf.yaml b/tests/components/online_image/test.esp32-idf.yaml index 3f01009812..d4af284151 100644 --- a/tests/components/online_image/test.esp32-idf.yaml +++ b/tests/components/online_image/test.esp32-idf.yaml @@ -1,4 +1,3 @@ <<: !include common-esp32.yaml http_request: - diff --git a/tests/components/openthread/test.esp32-c6-idf.yaml b/tests/components/openthread/test.esp32-c6-idf.yaml index bbcf48efa5..da5339fb39 100644 --- a/tests/components/openthread/test.esp32-c6-idf.yaml +++ b/tests/components/openthread/test.esp32-c6-idf.yaml @@ -11,4 +11,3 @@ openthread: pskc: 0xc23a76e98f1a6483639b1ac1271e2e27 mesh_local_prefix: fd53:145f:ed22:ad81::/64 force_dataset: true - diff --git a/tests/components/packages/test.esp32-ard.yaml b/tests/components/packages/test.esp32-ard.yaml index 7d0ab2b905..9e4ceb09d6 100644 --- a/tests/components/packages/test.esp32-ard.yaml +++ b/tests/components/packages/test.esp32-ard.yaml @@ -9,4 +9,3 @@ packages: file: common.yaml ref: dev refresh: 1d - diff --git a/tests/components/packages/test.esp32-idf.yaml b/tests/components/packages/test.esp32-idf.yaml index 8c0a34bb1a..5bf40822dc 100644 --- a/tests/components/packages/test.esp32-idf.yaml +++ b/tests/components/packages/test.esp32-idf.yaml @@ -11,4 +11,3 @@ packages: file: common.yaml ref: dev refresh: 1d - diff --git a/tests/components/qspi_dbi/common.yaml b/tests/components/qspi_dbi/common.yaml index 655af304af..f4b15f2b90 100644 --- a/tests/components/qspi_dbi/common.yaml +++ b/tests/components/qspi_dbi/common.yaml @@ -41,4 +41,3 @@ display: - delay 120ms - [0x29] - delay 20ms - diff --git a/tests/components/spi/test.esp32-p4-idf.yaml b/tests/components/spi/test.esp32-p4-idf.yaml index 061e3dd44a..93626dc1d5 100644 --- a/tests/components/spi/test.esp32-p4-idf.yaml +++ b/tests/components/spi/test.esp32-p4-idf.yaml @@ -35,4 +35,3 @@ spi: interface: any clk_pin: 8 mosi_pin: 9 - diff --git a/tests/components/spi/test.esp32-s3-idf.yaml b/tests/components/spi/test.esp32-s3-idf.yaml index 061e3dd44a..93626dc1d5 100644 --- a/tests/components/spi/test.esp32-s3-idf.yaml +++ b/tests/components/spi/test.esp32-s3-idf.yaml @@ -35,4 +35,3 @@ spi: interface: any clk_pin: 8 mosi_pin: 9 - diff --git a/tests/components/udp/common.yaml b/tests/components/udp/common.yaml index 79da02a692..96224d0d1f 100644 --- a/tests/components/udp/common.yaml +++ b/tests/components/udp/common.yaml @@ -17,4 +17,3 @@ udp: id: my_udp data: !lambda |- return std::vector{1,3,4,5,6}; - diff --git a/tests/components/web_server/test_ota.esp32-idf.yaml b/tests/components/web_server/test_ota.esp32-idf.yaml index 37838b3d34..99873aa27b 100644 --- a/tests/components/web_server/test_ota.esp32-idf.yaml +++ b/tests/components/web_server/test_ota.esp32-idf.yaml @@ -27,4 +27,3 @@ logger: logs: web_server: VERBOSE web_server_idf: VERBOSE - diff --git a/tests/components/wk2132_i2c/test.esp32-ard.yaml b/tests/components/wk2132_i2c/test.esp32-ard.yaml index 94552c5a40..3b761d3fc1 100644 --- a/tests/components/wk2132_i2c/test.esp32-ard.yaml +++ b/tests/components/wk2132_i2c/test.esp32-ard.yaml @@ -3,4 +3,3 @@ substitutions: sda_pin: GPIO21 <<: !include common.yaml - diff --git a/tests/integration/fixtures/areas_and_devices.yaml b/tests/integration/fixtures/areas_and_devices.yaml index 4a327b73a1..6bf1519c79 100644 --- a/tests/integration/fixtures/areas_and_devices.yaml +++ b/tests/integration/fixtures/areas_and_devices.yaml @@ -54,4 +54,3 @@ sensor: device_id: smart_switch_device lambda: return 4.0; update_interval: 0.1s - diff --git a/tests/integration/fixtures/device_id_in_state.yaml b/tests/integration/fixtures/device_id_in_state.yaml index f2e320a2e2..c8548617b8 100644 --- a/tests/integration/fixtures/device_id_in_state.yaml +++ b/tests/integration/fixtures/device_id_in_state.yaml @@ -82,4 +82,3 @@ output: write_action: - lambda: |- ESP_LOGD("test", "Light output: %d", state); - diff --git a/tests/integration/fixtures/large_message_batching.yaml b/tests/integration/fixtures/large_message_batching.yaml index 1b2d817cd4..e491da0486 100644 --- a/tests/integration/fixtures/large_message_batching.yaml +++ b/tests/integration/fixtures/large_message_batching.yaml @@ -134,4 +134,3 @@ switch: name: "Test Switch" id: test_switch optimistic: true - diff --git a/tests/script/test_clang_tidy_hash.py b/tests/script/test_clang_tidy_hash.py index dbcb477a4f..e4d4b40473 100644 --- a/tests/script/test_clang_tidy_hash.py +++ b/tests/script/test_clang_tidy_hash.py @@ -158,7 +158,7 @@ def test_write_hash() -> None: mock_write.assert_called_once() args = mock_write.call_args[0] assert str(args[0]).endswith(".clang-tidy.hash") - assert args[1] == hash_value + assert args[1] == hash_value.strip() + "\n" @pytest.mark.parametrize( diff --git a/tests/unit_tests/fixtures/substitutions/.gitignore b/tests/unit_tests/fixtures/substitutions/.gitignore index 0b15cdb2b7..897c9da86c 100644 --- a/tests/unit_tests/fixtures/substitutions/.gitignore +++ b/tests/unit_tests/fixtures/substitutions/.gitignore @@ -1 +1 @@ -*.received.yaml \ No newline at end of file +*.received.yaml From e3da197adfb986ca44a652e48192b821e10c63e2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 14:52:03 -1000 Subject: [PATCH 53/90] Remove yamllint job from CI since its now handled by pre-commit job (#9500) --- .github/workflows/ci.yml | 2 +- .github/workflows/yaml-lint.yml | 25 ------------------------- .pre-commit-config.yaml | 2 +- 3 files changed, 2 insertions(+), 27 deletions(-) delete mode 100644 .github/workflows/yaml-lint.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16943861ea..94e490a4c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -489,7 +489,7 @@ jobs: cache-key: ${{ needs.common.outputs.cache-key }} - uses: pre-commit/action@v3.0.1 env: - SKIP: pylint,clang-tidy-hash,yamllint + SKIP: pylint,clang-tidy-hash - uses: pre-commit-ci/lite-action@v1.1.0 if: always() diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml deleted file mode 100644 index ed9b4407a2..0000000000 --- a/.github/workflows/yaml-lint.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: YAML lint - -on: - push: - branches: [dev, beta, release] - paths: - - "**.yaml" - - "**.yml" - pull_request: - paths: - - "**.yaml" - - "**.yml" - -jobs: - yamllint: - name: yamllint - runs-on: ubuntu-latest - steps: - - name: Check out code from GitHub - uses: actions/checkout@v4.2.2 - - name: Run yamllint - uses: frenck/action-yamllint@v1.5.0 - with: - strict: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a522d9d36..118253861d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ ci: autoupdate_commit_msg: 'pre-commit: autoupdate' autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit # Skip hooks that have issues in pre-commit CI environment - skip: [pylint, clang-tidy-hash, yamllint] + skip: [pylint, clang-tidy-hash] repos: - repo: https://github.com/astral-sh/ruff-pre-commit From 8f58ca3a2a5c04243ef1aada88fbd499c4f1e2f0 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 12:09:18 +1000 Subject: [PATCH 54/90] [online_image] Support `byte_order` (#9502) --- esphome/components/online_image/__init__.py | 5 ++++- esphome/components/online_image/online_image.cpp | 16 +++++++++++----- esphome/components/online_image/online_image.h | 7 ++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index 3f15db6e50..7a6d25bc7d 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -2,7 +2,7 @@ import logging from esphome import automation import esphome.codegen as cg -from esphome.components.const import CONF_REQUEST_HEADERS +from esphome.components.const import CONF_BYTE_ORDER, CONF_REQUEST_HEADERS from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( CONF_INVERT_ALPHA, @@ -11,6 +11,7 @@ from esphome.components.image import ( Image_, get_image_type_enum, get_transparency_enum, + validate_settings, ) import esphome.config_validation as cv from esphome.const import ( @@ -161,6 +162,7 @@ CONFIG_SCHEMA = cv.Schema( rp2040_arduino=cv.Version(0, 0, 0), host=cv.Version(0, 0, 0), ), + validate_settings, ) ) @@ -213,6 +215,7 @@ async def to_code(config): get_image_type_enum(config[CONF_TYPE]), transparent, config[CONF_BUFFER_SIZE], + config.get(CONF_BYTE_ORDER) != "LITTLE_ENDIAN", ) await cg.register_component(var, config) await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index d0c743ef93..4e2ecc2c77 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -35,14 +35,15 @@ inline bool is_color_on(const Color &color) { } OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type, - image::Transparency transparency, uint32_t download_buffer_size) + image::Transparency transparency, uint32_t download_buffer_size, bool is_big_endian) : Image(nullptr, 0, 0, type, transparency), buffer_(nullptr), download_buffer_(download_buffer_size), download_buffer_initial_size_(download_buffer_size), format_(format), fixed_width_(width), - fixed_height_(height) { + fixed_height_(height), + is_big_endian_(is_big_endian) { this->set_url(url); } @@ -296,7 +297,7 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { break; } case ImageType::IMAGE_TYPE_GRAYSCALE: { - uint8_t gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); + auto gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) { if (gray == 1) { gray = 0; @@ -314,8 +315,13 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { case ImageType::IMAGE_TYPE_RGB565: { this->map_chroma_key(color); uint16_t col565 = display::ColorUtil::color_to_565(color); - this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); - this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + if (this->is_big_endian_) { + this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); + this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + } else { + this->buffer_[pos + 0] = static_cast(col565 & 0xFF); + this->buffer_[pos + 1] = static_cast((col565 >> 8) & 0xFF); + } if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { this->buffer_[pos + 2] = color.w; } diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 6a2144538f..3326cbe8d6 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -50,7 +50,7 @@ class OnlineImage : public PollingComponent, * @param buffer_size Size of the buffer used to download the image. */ OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type, - image::Transparency transparency, uint32_t buffer_size); + image::Transparency transparency, uint32_t buffer_size, bool is_big_endian); void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; @@ -164,6 +164,11 @@ class OnlineImage : public PollingComponent, const int fixed_width_; /** height requested on configuration, or 0 if non specified. */ const int fixed_height_; + /** + * Whether the image is stored in big-endian format. + * This is used to determine how to store 16 bit colors in the buffer. + */ + bool is_big_endian_; /** * Actual width of the current image. If fixed_width_ is specified, * this will be equal to it; otherwise it will be set once the decoding From 9ae45ba8aa44c5e41fba3478dc91d9680dfb3edf Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 15 Jul 2025 03:11:10 +0100 Subject: [PATCH 55/90] [json] Bump ArduinoJson library to 7.4.2 (#8857) Co-authored-by: J. Nick Koston Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../update/http_request_update.cpp | 14 +-- esphome/components/json/__init__.py | 2 +- esphome/components/json/json_util.cpp | 119 +++++++++--------- .../components/light/light_json_schema.cpp | 33 ++--- .../mqtt/mqtt_alarm_control_panel.cpp | 3 +- .../components/mqtt/mqtt_binary_sensor.cpp | 1 + esphome/components/mqtt/mqtt_button.cpp | 5 +- esphome/components/mqtt/mqtt_client.cpp | 2 + esphome/components/mqtt/mqtt_climate.cpp | 10 +- esphome/components/mqtt/mqtt_component.cpp | 4 +- esphome/components/mqtt/mqtt_cover.cpp | 1 + esphome/components/mqtt/mqtt_date.cpp | 7 +- esphome/components/mqtt/mqtt_datetime.cpp | 13 +- esphome/components/mqtt/mqtt_event.cpp | 9 +- esphome/components/mqtt/mqtt_fan.cpp | 1 + esphome/components/mqtt/mqtt_light.cpp | 12 +- esphome/components/mqtt/mqtt_lock.cpp | 4 +- esphome/components/mqtt/mqtt_number.cpp | 1 + esphome/components/mqtt/mqtt_select.cpp | 3 +- esphome/components/mqtt/mqtt_sensor.cpp | 4 +- esphome/components/mqtt/mqtt_switch.cpp | 4 +- esphome/components/mqtt/mqtt_text.cpp | 1 + esphome/components/mqtt/mqtt_text_sensor.cpp | 4 +- esphome/components/mqtt/mqtt_time.cpp | 7 +- esphome/components/mqtt/mqtt_update.cpp | 1 + esphome/components/mqtt/mqtt_valve.cpp | 4 +- esphome/components/web_server/web_server.cpp | 22 ++-- platformio.ini | 2 +- 28 files changed, 164 insertions(+), 129 deletions(-) diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 202c7b88b2..06aa6da6a4 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -83,7 +83,7 @@ void HttpRequestUpdate::update_task(void *params) { container.reset(); // Release ownership of the container's shared_ptr valid = json::parse_json(response, [this_update](JsonObject root) -> bool { - if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { + if (!root["name"].is() || !root["version"].is() || !root["builds"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } @@ -91,26 +91,26 @@ void HttpRequestUpdate::update_task(void *params) { this_update->update_info_.latest_version = root["version"].as(); for (auto build : root["builds"].as()) { - if (!build.containsKey("chipFamily")) { + if (!build["chipFamily"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } if (build["chipFamily"] == ESPHOME_VARIANT) { - if (!build.containsKey("ota")) { + if (!build["ota"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - auto ota = build["ota"]; - if (!ota.containsKey("path") || !ota.containsKey("md5")) { + JsonObject ota = build["ota"].as(); + if (!ota["path"].is() || !ota["md5"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } this_update->update_info_.firmware_url = ota["path"].as(); this_update->update_info_.md5 = ota["md5"].as(); - if (ota.containsKey("summary")) + if (ota["summary"].is()) this_update->update_info_.summary = ota["summary"].as(); - if (ota.containsKey("release_url")) + if (ota["release_url"].is()) this_update->update_info_.release_url = ota["release_url"].as(); return true; diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 6a0e4c50d2..9773bf67ce 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -12,6 +12,6 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(1.0) async def to_code(config): - cg.add_library("bblanchon/ArduinoJson", "6.18.5") + cg.add_library("bblanchon/ArduinoJson", "7.4.2") cg.add_define("USE_JSON") cg.add_global(json_ns.using) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 6c66476dc1..94c531222a 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,83 +1,76 @@ #include "json_util.h" #include "esphome/core/log.h" +// ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h + namespace esphome { namespace json { static const char *const TAG = "json"; -static std::vector global_json_build_buffer; // NOLINT -static const auto ALLOCATOR = RAMAllocator(RAMAllocator::ALLOC_INTERNAL); +// Build an allocator for the JSON Library using the RAMAllocator class +struct SpiRamAllocator : ArduinoJson::Allocator { + void *allocate(size_t size) override { return this->allocator_.allocate(size); } + + void deallocate(void *pointer) override { + // ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate. + // RAMAllocator::deallocate() requires the size, which we don't have access to here. + // RAMAllocator::deallocate implementation just calls free() regardless of whether + // the memory was allocated with heap_caps_malloc or malloc. + // This is safe because ESP-IDF's heap implementation internally tracks the memory region + // and routes free() to the appropriate heap. + free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) + } + + void *reallocate(void *ptr, size_t new_size) override { + return this->allocator_.reallocate(static_cast(ptr), new_size); + } + + protected: + RAMAllocator allocator_{RAMAllocator(RAMAllocator::NONE)}; +}; std::string build_json(const json_build_t &f) { - // Here we are allocating up to 5kb of memory, - // with the heap size minus 2kb to be safe if less than 5kb - // as we can not have a true dynamic sized document. - // The excess memory is freed below with `shrinkToFit()` - auto free_heap = ALLOCATOR.get_max_free_block_size(); - size_t request_size = std::min(free_heap, (size_t) 512); - while (true) { - ESP_LOGV(TAG, "Attempting to allocate %zu bytes for JSON serialization", request_size); - DynamicJsonDocument json_document(request_size); - if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, largest free heap block: %zu bytes", - request_size, free_heap); - return "{}"; - } - JsonObject root = json_document.to(); - f(root); - if (json_document.overflowed()) { - if (request_size == free_heap) { - ESP_LOGE(TAG, "Could not allocate memory for document! Overflowed largest free heap block: %zu bytes", - free_heap); - return "{}"; - } - request_size = std::min(request_size * 2, free_heap); - continue; - } - json_document.shrinkToFit(); - ESP_LOGV(TAG, "Size after shrink %zu bytes", json_document.capacity()); - std::string output; - serializeJson(json_document, output); - return output; + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + auto doc_allocator = SpiRamAllocator(); + JsonDocument json_document(&doc_allocator); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return "{}"; } + JsonObject root = json_document.to(); + f(root); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return "{}"; + } + std::string output; + serializeJson(json_document, output); + return output; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } bool parse_json(const std::string &data, const json_parse_t &f) { - // Here we are allocating 1.5 times the data size, - // with the heap size minus 2kb to be safe if less than that - // as we can not have a true dynamic sized document. - // The excess memory is freed below with `shrinkToFit()` - auto free_heap = ALLOCATOR.get_max_free_block_size(); - size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); - while (true) { - DynamicJsonDocument json_document(request_size); - if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, free heap: %zu", request_size, - free_heap); - return false; - } - DeserializationError err = deserializeJson(json_document, data); - json_document.shrinkToFit(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + auto doc_allocator = SpiRamAllocator(); + JsonDocument json_document(&doc_allocator); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return false; + } + DeserializationError err = deserializeJson(json_document, data); - JsonObject root = json_document.as(); + JsonObject root = json_document.as(); - if (err == DeserializationError::Ok) { - return f(root); - } else if (err == DeserializationError::NoMemory) { - if (request_size * 2 >= free_heap) { - ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); - return false; - } - ESP_LOGV(TAG, "Increasing memory allocation."); - request_size *= 2; - continue; - } else { - ESP_LOGE(TAG, "Parse error: %s", err.c_str()); - return false; - } - }; + if (err == DeserializationError::Ok) { + return f(root); + } else if (err == DeserializationError::NoMemory) { + ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); + return false; + } + ESP_LOGE(TAG, "Parse error: %s", err.c_str()); return false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } } // namespace json diff --git a/esphome/components/light/light_json_schema.cpp b/esphome/components/light/light_json_schema.cpp index 6f8cc11f25..26615bae5c 100644 --- a/esphome/components/light/light_json_schema.cpp +++ b/esphome/components/light/light_json_schema.cpp @@ -9,6 +9,7 @@ namespace light { // See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema void LightJSONSchema::dump_json(LightState &state, JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (state.supports_effects()) root["effect"] = state.get_effect_name(); @@ -52,7 +53,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { if (values.get_color_mode() & ColorCapability::BRIGHTNESS) root["brightness"] = uint8_t(values.get_brightness() * 255); - JsonObject color = root.createNestedObject("color"); + JsonObject color = root["color"].to(); if (values.get_color_mode() & ColorCapability::RGB) { color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255); color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255); @@ -73,7 +74,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { } void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject root) { - if (root.containsKey("state")) { + if (root["state"].is()) { auto val = parse_on_off(root["state"]); switch (val) { case PARSE_ON: @@ -90,40 +91,40 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO } } - if (root.containsKey("brightness")) { + if (root["brightness"].is()) { call.set_brightness(float(root["brightness"]) / 255.0f); } - if (root.containsKey("color")) { + if (root["color"].is()) { JsonObject color = root["color"]; // HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness. float max_rgb = 0.0f; - if (color.containsKey("r")) { + if (color["r"].is()) { float r = float(color["r"]) / 255.0f; max_rgb = fmaxf(max_rgb, r); call.set_red(r); } - if (color.containsKey("g")) { + if (color["g"].is()) { float g = float(color["g"]) / 255.0f; max_rgb = fmaxf(max_rgb, g); call.set_green(g); } - if (color.containsKey("b")) { + if (color["b"].is()) { float b = float(color["b"]) / 255.0f; max_rgb = fmaxf(max_rgb, b); call.set_blue(b); } - if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) { + if (color["r"].is() || color["g"].is() || color["b"].is()) { call.set_color_brightness(max_rgb); } - if (color.containsKey("c")) { + if (color["c"].is()) { call.set_cold_white(float(color["c"]) / 255.0f); } - if (color.containsKey("w")) { + if (color["w"].is()) { // the HA scheme is ambiguous here, the same key is used for white channel in RGBW and warm // white channel in RGBWW. - if (color.containsKey("c")) { + if (color["c"].is()) { call.set_warm_white(float(color["w"]) / 255.0f); } else { call.set_white(float(color["w"]) / 255.0f); @@ -131,11 +132,11 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO } } - if (root.containsKey("white_value")) { // legacy API + if (root["white_value"].is()) { // legacy API call.set_white(float(root["white_value"]) / 255.0f); } - if (root.containsKey("color_temp")) { + if (root["color_temp"].is()) { call.set_color_temperature(float(root["color_temp"])); } } @@ -143,17 +144,17 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject root) { LightJSONSchema::parse_color_json(state, call, root); - if (root.containsKey("flash")) { + if (root["flash"].is()) { auto length = uint32_t(float(root["flash"]) * 1000); call.set_flash_length(length); } - if (root.containsKey("transition")) { + if (root["transition"].is()) { auto length = uint32_t(float(root["transition"]) * 1000); call.set_transition_length(length); } - if (root.containsKey("effect")) { + if (root["effect"].is()) { const char *effect = root["effect"]; call.set_effect(effect); } diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 0a38598679..94460c31a7 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -55,7 +55,8 @@ void MQTTAlarmControlPanelComponent::dump_config() { } void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to(); const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features(); if (acp_supported_features & ACP_FEAT_ARM_AWAY) { supported_features.add("arm_away"); diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 6d12e88391..2ce4928574 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -30,6 +30,7 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor } void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (!this->binary_sensor_->get_device_class().empty()) root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class(); if (this->binary_sensor_->is_status_binary_sensor()) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index 204f60fe67..c619a02344 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -31,9 +31,12 @@ void MQTTButtonComponent::dump_config() { } void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; - if (!this->button_->get_device_class().empty()) + if (!this->button_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } std::string MQTTButtonComponent::component_type() const { return "button"; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index ab7fd15a35..5b93789447 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -92,6 +92,7 @@ void MQTTClientComponent::send_device_info_() { std::string topic = "esphome/discover/"; topic.append(App.get_name()); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson this->publish_json( topic, [](JsonObject root) { @@ -147,6 +148,7 @@ void MQTTClientComponent::send_device_info_() { #endif }, 2, this->discovery_info_.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClientComponent::dump_config() { diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index a8768114a4..e16f097812 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -14,6 +14,7 @@ static const char *const TAG = "mqtt.climate"; using namespace esphome::climate; void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto traits = this->device_->get_traits(); // current_temperature_topic if (traits.get_supports_current_temperature()) { @@ -28,7 +29,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // mode_state_topic root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic(); // modes - JsonArray modes = root.createNestedArray(MQTT_MODES); + JsonArray modes = root[MQTT_MODES].to(); // sort array for nice UI in HA if (traits.supports_mode(CLIMATE_MODE_AUTO)) modes.add("auto"); @@ -89,7 +90,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // preset_mode_state_topic root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic(); // presets - JsonArray presets = root.createNestedArray("preset_modes"); + JsonArray presets = root["preset_modes"].to(); if (traits.supports_preset(CLIMATE_PRESET_HOME)) presets.add("home"); if (traits.supports_preset(CLIMATE_PRESET_AWAY)) @@ -119,7 +120,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // fan_mode_state_topic root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic(); // fan_modes - JsonArray fan_modes = root.createNestedArray("fan_modes"); + JsonArray fan_modes = root["fan_modes"].to(); if (traits.supports_fan_mode(CLIMATE_FAN_ON)) fan_modes.add("on"); if (traits.supports_fan_mode(CLIMATE_FAN_OFF)) @@ -150,7 +151,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // swing_mode_state_topic root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic(); // swing_modes - JsonArray swing_modes = root.createNestedArray("swing_modes"); + JsonArray swing_modes = root["swing_modes"].to(); if (traits.supports_swing_mode(CLIMATE_SWING_OFF)) swing_modes.add("off"); if (traits.supports_swing_mode(CLIMATE_SWING_BOTH)) @@ -163,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo config.state_topic = false; config.command_topic = false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClimateComponent::setup() { auto traits = this->device_->get_traits(); diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index eee5644c9d..b51f4d903e 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -70,6 +70,7 @@ bool MQTTComponent::send_discovery_() { ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), [this](JsonObject root) { @@ -155,7 +156,7 @@ bool MQTTComponent::send_discovery_() { } std::string node_area = App.get_area(); - JsonObject device_info = root.createNestedObject(MQTT_DEVICE); + JsonObject device_info = root[MQTT_DEVICE].to(); const auto mac = get_mac_address(); device_info[MQTT_DEVICE_IDENTIFIERS] = mac; device_info[MQTT_DEVICE_NAME] = node_friendly_name; @@ -192,6 +193,7 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac; }, this->qos_, discovery_info.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } uint8_t MQTTComponent::get_qos() const { return this->qos_; } diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 8d09d836f3..6fb61ee469 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -67,6 +67,7 @@ void MQTTCoverComponent::dump_config() { } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (!this->cover_->get_device_class().empty()) root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class(); diff --git a/esphome/components/mqtt/mqtt_date.cpp b/esphome/components/mqtt/mqtt_date.cpp index 088a4788ed..0f0a334ae7 100644 --- a/esphome/components/mqtt/mqtt_date.cpp +++ b/esphome/components/mqtt/mqtt_date.cpp @@ -20,13 +20,13 @@ MQTTDateComponent::MQTTDateComponent(DateEntity *date) : date_(date) {} void MQTTDateComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->date_->make_call(); - if (root.containsKey("year")) { + if (root["year"].is()) { call.set_year(root["year"]); } - if (root.containsKey("month")) { + if (root["month"].is()) { call.set_month(root["month"]); } - if (root.containsKey("day")) { + if (root["day"].is()) { call.set_day(root["day"]); } call.perform(); @@ -55,6 +55,7 @@ bool MQTTDateComponent::send_initial_state() { } bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) { return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["year"] = year; root["month"] = month; root["day"] = day; diff --git a/esphome/components/mqtt/mqtt_datetime.cpp b/esphome/components/mqtt/mqtt_datetime.cpp index 4ae6d0d416..5c56baabe0 100644 --- a/esphome/components/mqtt/mqtt_datetime.cpp +++ b/esphome/components/mqtt/mqtt_datetime.cpp @@ -20,22 +20,22 @@ MQTTDateTimeComponent::MQTTDateTimeComponent(DateTimeEntity *datetime) : datetim void MQTTDateTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->datetime_->make_call(); - if (root.containsKey("year")) { + if (root["year"].is()) { call.set_year(root["year"]); } - if (root.containsKey("month")) { + if (root["month"].is()) { call.set_month(root["month"]); } - if (root.containsKey("day")) { + if (root["day"].is()) { call.set_day(root["day"]); } - if (root.containsKey("hour")) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root.containsKey("minute")) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root.containsKey("second")) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform(); @@ -68,6 +68,7 @@ bool MQTTDateTimeComponent::send_initial_state() { bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["year"] = year; root["month"] = month; root["day"] = day; diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index cf0b90e3d6..f972d545c6 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -16,7 +16,8 @@ using namespace esphome::event; MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {} void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray event_types = root[MQTT_EVENT_TYPES].to(); for (const auto &event_type : this->event_->get_event_types()) event_types.add(event_type); @@ -40,8 +41,10 @@ void MQTTEventComponent::dump_config() { } bool MQTTEventComponent::publish_event_(const std::string &event_type) { - return this->publish_json(this->get_state_topic_(), - [event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; }); + return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + root[MQTT_EVENT_TYPE] = event_type; + }); } std::string MQTTEventComponent::component_type() const { return "event"; } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 35713bdab6..70e1ae3b4a 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -143,6 +143,7 @@ void MQTTFanComponent::dump_config() { bool MQTTFanComponent::send_initial_state() { return this->publish_state(); } void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (this->state_->get_traits().supports_direction()) { root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic(); root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic(); diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index f970da7d8c..4f5ff408a4 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -32,17 +32,21 @@ void MQTTJSONLightComponent::setup() { MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {} bool MQTTJSONLightComponent::publish_state_() { - return this->publish_json(this->get_state_topic_(), - [this](JsonObject root) { LightJSONSchema::dump_json(*this->state_, root); }); + return this->publish_json(this->get_state_topic_(), [this](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + LightJSONSchema::dump_json(*this->state_, root); + }); } LightState *MQTTJSONLightComponent::get_state() const { return this->state_; } void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["schema"] = "json"; auto traits = this->state_->get_traits(); root[MQTT_COLOR_MODE] = true; - JsonArray color_modes = root.createNestedArray("supported_color_modes"); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray color_modes = root["supported_color_modes"].to(); if (traits.supports_color_mode(ColorMode::ON_OFF)) color_modes.add("onoff"); if (traits.supports_color_mode(ColorMode::BRIGHTNESS)) @@ -67,7 +71,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery if (this->state_->supports_effects()) { root["effect"] = true; - JsonArray effect_list = root.createNestedArray(MQTT_EFFECT_LIST); + JsonArray effect_list = root[MQTT_EFFECT_LIST].to(); for (auto *effect : this->state_->get_effects()) effect_list.add(effect->get_name()); effect_list.add("None"); diff --git a/esphome/components/mqtt/mqtt_lock.cpp b/esphome/components/mqtt/mqtt_lock.cpp index f4a5126d0c..0412624983 100644 --- a/esphome/components/mqtt/mqtt_lock.cpp +++ b/esphome/components/mqtt/mqtt_lock.cpp @@ -38,8 +38,10 @@ void MQTTLockComponent::dump_config() { std::string MQTTLockComponent::component_type() const { return "lock"; } const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; } void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (this->lock_->traits.get_assumed_state()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (this->lock_->traits.get_assumed_state()) { root[MQTT_OPTIMISTIC] = true; + } if (this->lock_->traits.get_supports_open()) root[MQTT_PAYLOAD_OPEN] = "OPEN"; } diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index 3a6ea97967..a44632ff30 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -40,6 +40,7 @@ const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { const auto &traits = number_->traits; // https://www.home-assistant.io/integrations/number.mqtt/ + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MAX] = traits.get_max_value(); root[MQTT_STEP] = traits.get_step(); diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index ea5130f823..b851348306 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -35,7 +35,8 @@ const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_ void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { const auto &traits = select_->traits; // https://www.home-assistant.io/integrations/select.mqtt/ - JsonArray options = root.createNestedArray(MQTT_OPTIONS); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray options = root[MQTT_OPTIONS].to(); for (const auto &option : traits.get_options()) options.add(option); diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 2cbc291ccf..9324ea9bb1 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -44,8 +44,10 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->sensor_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->sensor_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + } if (!this->sensor_->get_unit_of_measurement().empty()) root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement(); diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index 3fd578825a..8b1323bdb2 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -45,8 +45,10 @@ void MQTTSwitchComponent::dump_config() { std::string MQTTSwitchComponent::component_type() const { return "switch"; } const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; } void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (this->switch_->assumed_state()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (this->switch_->assumed_state()) { root[MQTT_OPTIMISTIC] = true; + } } bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); } diff --git a/esphome/components/mqtt/mqtt_text.cpp b/esphome/components/mqtt/mqtt_text.cpp index cb852b64cd..5ab0ca9688 100644 --- a/esphome/components/mqtt/mqtt_text.cpp +++ b/esphome/components/mqtt/mqtt_text.cpp @@ -34,6 +34,7 @@ std::string MQTTTextComponent::component_type() const { return "text"; } const EntityBase *MQTTTextComponent::get_entity() const { return this->text_; } void MQTTTextComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson switch (this->text_->traits.get_mode()) { case TEXT_MODE_TEXT: root[MQTT_MODE] = "text"; diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index b0754bc8b3..0cc5de07a3 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -15,8 +15,10 @@ using namespace esphome::text_sensor; MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {} void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->sensor_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->sensor_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + } config.command_topic = false; } void MQTTTextSensor::setup() { diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp index 332ef53cbc..0c95bd8147 100644 --- a/esphome/components/mqtt/mqtt_time.cpp +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {} void MQTTTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->time_->make_call(); - if (root.containsKey("hour")) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root.containsKey("minute")) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root.containsKey("second")) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform(); @@ -55,6 +55,7 @@ bool MQTTTimeComponent::send_initial_state() { } bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) { return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["hour"] = hour; root["minute"] = minute; root["second"] = second; diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 2ed8faf074..5d4807c7f3 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -41,6 +41,7 @@ bool MQTTUpdateComponent::publish_state() { } void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["schema"] = "json"; root[MQTT_PAYLOAD_INSTALL] = "INSTALL"; } diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 85e06fe79c..551398cf42 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -49,8 +49,10 @@ void MQTTValveComponent::dump_config() { } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->valve_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->valve_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class(); + } auto traits = this->valve_->get_traits(); if (traits.get_is_assumed_state()) { diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index da5c6b7cd7..9ec667dbc5 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -792,7 +792,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi light::LightJSONSchema::dump_json(*obj, root); if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("effects"); + JsonArray opt = root["effects"].to(); opt.add("None"); for (auto const &option : obj->get_effects()) { opt.add(option->get_name()); @@ -1238,7 +1238,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("option"); + JsonArray opt = root["option"].to(); for (auto &option : obj->traits.get_options()) { opt.add(option); } @@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); @@ -1330,32 +1331,32 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf char buf[16]; if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("modes"); + JsonArray opt = root["modes"].to(); for (climate::ClimateMode m : traits.get_supported_modes()) opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); if (!traits.get_supported_custom_fan_modes().empty()) { - JsonArray opt = root.createNestedArray("fan_modes"); + JsonArray opt = root["fan_modes"].to(); for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { - JsonArray opt = root.createNestedArray("custom_fan_modes"); + JsonArray opt = root["custom_fan_modes"].to(); for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) opt.add(custom_fan_mode); } if (traits.get_supports_swing_modes()) { - JsonArray opt = root.createNestedArray("swing_modes"); + JsonArray opt = root["swing_modes"].to(); for (auto swing_mode : traits.get_supported_swing_modes()) opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode))); } if (traits.get_supports_presets() && obj->preset.has_value()) { - JsonArray opt = root.createNestedArray("presets"); + JsonArray opt = root["presets"].to(); for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { - JsonArray opt = root.createNestedArray("custom_presets"); + JsonArray opt = root["custom_presets"].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); } @@ -1407,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["state"] = root["target_temperature"]; } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif @@ -1635,7 +1637,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty root["event_type"] = event_type; } if (start_config == DETAIL_ALL) { - JsonArray event_types = root.createNestedArray("event_types"); + JsonArray event_types = root["event_types"].to(); for (auto const &event_type : obj->get_event_types()) { event_types.add(event_type); } @@ -1682,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; @@ -1707,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c this->add_sorting_info_(root, obj); } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif diff --git a/platformio.ini b/platformio.ini index 54c72eb28d..f9e4e31ece 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,7 +35,7 @@ build_flags = lib_deps = esphome/noise-c@0.1.10 ; api improv/Improv@1.2.4 ; improv_serial / esp32_improv - bblanchon/ArduinoJson@6.18.5 ; json + bblanchon/ArduinoJson@7.4.2 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 pavlodn/HaierProtocol@0.9.31 ; haier From a572d4eb4752f1538260b33715a05362b9c63a89 Mon Sep 17 00:00:00 2001 From: skyegecko Date: Tue, 15 Jul 2025 04:15:47 +0200 Subject: [PATCH 56/90] [fan] Do not save state for fan if configured as NO_RESTORE (#9472) --- esphome/components/fan/fan.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 25f710f893..82fc5319e0 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -177,6 +177,10 @@ optional Fan::restore_state_() { return {}; } void Fan::save_state_() { + if (this->restore_mode_ == FanRestoreMode::NO_RESTORE) { + return; + } + FanRestoreState state{}; state.state = this->state; state.oscillating = this->oscillating; From d3d1ba553df89674213e92a03535120d178f9dd2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 16:17:56 -1000 Subject: [PATCH 57/90] Fix blocked CI cancellation caused by always() in clang-tidy workflow (#9503) --- .github/workflows/ci.yml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 94e490a4c8..2f63a16844 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -246,30 +246,13 @@ jobs: . venv/bin/activate pytest -vv --no-cov --tb=native -n auto tests/integration/ - clang-tidy-deps: - name: Clang-tidy dependencies - runs-on: ubuntu-24.04 - needs: - - common - - ci-custom - - pytest - - determine-jobs - if: | - always() && - needs.determine-jobs.outputs.clang-tidy == 'true' - steps: - - run: echo "All clang-tidy dependencies ready" - clang-tidy: name: ${{ matrix.name }} runs-on: ubuntu-24.04 needs: - - clang-tidy-deps + - common - determine-jobs - if: | - always() && - needs.determine-jobs.outputs.clang-tidy == 'true' && - needs.clang-tidy-deps.result == 'success' + if: needs.determine-jobs.outputs.clang-tidy == 'true' env: GH_TOKEN: ${{ github.token }} strategy: @@ -502,7 +485,6 @@ jobs: - pylint - pytest - integration-tests - - clang-tidy-deps - clang-tidy - determine-jobs - test-build-components From 778b586d7805c90219723297bc0e3adba0f43e0f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 16:49:12 -1000 Subject: [PATCH 58/90] Fix LibreTiny compilation error by updating ESPAsyncWebServer and dependencies (#9492) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/async_tcp/__init__.py | 2 +- esphome/components/web_server_base/__init__.py | 2 +- platformio.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 29097ce1b6..4a469fa0e0 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -31,7 +31,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: # https://github.com/ESP32Async/AsyncTCP - cg.add_library("ESP32Async/AsyncTCP", "3.4.4") + cg.add_library("ESP32Async/AsyncTCP", "3.4.5") elif CORE.is_esp8266: # https://github.com/ESP32Async/ESPAsyncTCP cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0") diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 754bf7d433..9f3371c233 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -40,4 +40,4 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("ESP8266WiFi", None) # https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json - cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.8") + cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10") diff --git a/platformio.ini b/platformio.ini index f9e4e31ece..8fcc578103 100644 --- a/platformio.ini +++ b/platformio.ini @@ -235,7 +235,7 @@ build_flags = -DUSE_ZEPHYR -DUSE_NRF52 lib_deps = - bblanchon/ArduinoJson@7.0.0 ; json + bblanchon/ArduinoJson@7.4.2 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code pavlodn/HaierProtocol@0.9.31 ; haier functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 From c2f7dcfa6dccc2a9f742708df9fa64e9a1117caf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:36:07 +1000 Subject: [PATCH 59/90] [captive_portal] Add test case for libretiny (#9457) Co-authored-by: J. Nick Koston --- tests/components/captive_portal/test.bk72xx-ard.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/components/captive_portal/test.bk72xx-ard.yaml diff --git a/tests/components/captive_portal/test.bk72xx-ard.yaml b/tests/components/captive_portal/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/captive_portal/test.bk72xx-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 0f15250f12a440245695bc03b715b6f10265b904 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 14 Jul 2025 22:43:00 -0500 Subject: [PATCH 60/90] [opentherm.output] Fix ``lerp`` (#9506) --- esphome/components/opentherm/output/output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/opentherm/output/output.cpp b/esphome/components/opentherm/output/output.cpp index f820dc76f1..486aa0d4e7 100644 --- a/esphome/components/opentherm/output/output.cpp +++ b/esphome/components/opentherm/output/output.cpp @@ -10,7 +10,7 @@ void opentherm::OpenthermOutput::write_state(float state) { ESP_LOGD(TAG, "Received state: %.2f. Min value: %.2f, max value: %.2f", state, min_value_, max_value_); this->state = state < 0.003 && this->zero_means_zero_ ? 0.0 - : clamp(lerp(state, min_value_, max_value_), min_value_, max_value_); + : clamp(std::lerp(min_value_, max_value_, state), min_value_, max_value_); this->has_state_ = true; ESP_LOGD(TAG, "Output %s set to %.2f", this->id_, this->state); } From 84349b6d05a2148375574a716beb1b9b04d7abf7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 14 Jul 2025 22:45:38 -0500 Subject: [PATCH 61/90] [servo] Fix ``lerp`` (#9507) --- esphome/components/servo/servo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index b8546d345c..b4511de2d0 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -88,9 +88,9 @@ void Servo::internal_write(float value) { value = clamp(value, -1.0f, 1.0f); float level; if (value < 0.0) { - level = lerp(-value, this->idle_level_, this->min_level_); + level = std::lerp(this->idle_level_, this->min_level_, -value); } else { - level = lerp(value, this->idle_level_, this->max_level_); + level = std::lerp(this->idle_level_, this->max_level_, value); } this->output_->set_level(level); this->current_value_ = value; From 63b8a219e606bf3b2d7b6321b057633d4926b0db Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 20:26:39 -1000 Subject: [PATCH 62/90] Include entire platformio.ini in clang-tidy hash calculation (#9509) --- .clang-tidy.hash | 2 +- script/clang_tidy_hash.py | 27 ++------------ tests/script/test_clang_tidy_hash.py | 55 ++++++---------------------- 3 files changed, 17 insertions(+), 67 deletions(-) diff --git a/.clang-tidy.hash b/.clang-tidy.hash index d36ae1f164..18be8d78a9 100644 --- a/.clang-tidy.hash +++ b/.clang-tidy.hash @@ -1 +1 @@ -a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a +07f621354fe1350ba51953c80273cd44a04aa44f15cc30bd7b8fe2a641427b7a diff --git a/script/clang_tidy_hash.py b/script/clang_tidy_hash.py index 8cc135f5d0..19eb2a825e 100755 --- a/script/clang_tidy_hash.py +++ b/script/clang_tidy_hash.py @@ -62,26 +62,6 @@ def get_clang_tidy_version_from_requirements() -> str: return "clang-tidy version not found" -def extract_platformio_flags() -> str: - """Extract clang-tidy related flags from platformio.ini""" - flags: list[str] = [] - in_clangtidy_section = False - - platformio_path = Path(__file__).parent.parent / "platformio.ini" - lines = read_file_lines(platformio_path) - for line in lines: - line = line.strip() - if line.startswith("[flags:clangtidy]"): - in_clangtidy_section = True - continue - elif line.startswith("[") and in_clangtidy_section: - break - elif in_clangtidy_section and line and not line.startswith("#"): - flags.append(line) - - return "\n".join(sorted(flags)) - - def read_file_bytes(path: Path) -> bytes: """Read bytes from a file.""" with open(path, "rb") as f: @@ -101,9 +81,10 @@ def calculate_clang_tidy_hash() -> str: version = get_clang_tidy_version_from_requirements() hasher.update(version.encode()) - # Hash relevant platformio.ini sections - pio_flags = extract_platformio_flags() - hasher.update(pio_flags.encode()) + # Hash the entire platformio.ini file + platformio_path = Path(__file__).parent.parent / "platformio.ini" + platformio_content = read_file_bytes(platformio_path) + hasher.update(platformio_content) return hasher.hexdigest() diff --git a/tests/script/test_clang_tidy_hash.py b/tests/script/test_clang_tidy_hash.py index e4d4b40473..7b66a69adb 100644 --- a/tests/script/test_clang_tidy_hash.py +++ b/tests/script/test_clang_tidy_hash.py @@ -44,67 +44,36 @@ def test_get_clang_tidy_version_from_requirements( assert result == expected -@pytest.mark.parametrize( - ("platformio_content", "expected_flags"), - [ - ( - "[env:esp32]\n" - "platform = espressif32\n" - "\n" - "[flags:clangtidy]\n" - "build_flags = -Wall\n" - "extra_flags = -Wextra\n" - "\n" - "[env:esp8266]\n", - "build_flags = -Wall\nextra_flags = -Wextra", - ), - ( - "[flags:clangtidy]\n# Comment line\nbuild_flags = -O2\n\n[next_section]\n", - "build_flags = -O2", - ), - ( - "[flags:clangtidy]\nflag_c = -std=c99\nflag_b = -Wall\nflag_a = -O2\n", - "flag_a = -O2\nflag_b = -Wall\nflag_c = -std=c99", # Sorted - ), - ( - "[env:esp32]\nplatform = espressif32\n", # No clangtidy section - "", - ), - ], -) -def test_extract_platformio_flags(platformio_content: str, expected_flags: str) -> None: - """Test extracting clang-tidy flags from platformio.ini.""" - # Mock read_file_lines to return our test content - with patch("clang_tidy_hash.read_file_lines") as mock_read: - mock_read.return_value = platformio_content.splitlines(keepends=True) - - result = clang_tidy_hash.extract_platformio_flags() - - assert result == expected_flags - - def test_calculate_clang_tidy_hash() -> None: """Test calculating hash from all configuration sources.""" clang_tidy_content = b"Checks: '-*,readability-*'\n" requirements_version = "clang-tidy==18.1.5" - pio_flags = "build_flags = -Wall" + platformio_content = b"[env:esp32]\nplatform = espressif32\n" # Expected hash calculation expected_hasher = hashlib.sha256() expected_hasher.update(clang_tidy_content) expected_hasher.update(requirements_version.encode()) - expected_hasher.update(pio_flags.encode()) + expected_hasher.update(platformio_content) expected_hash = expected_hasher.hexdigest() # Mock the dependencies with ( - patch("clang_tidy_hash.read_file_bytes", return_value=clang_tidy_content), + patch("clang_tidy_hash.read_file_bytes") as mock_read_bytes, patch( "clang_tidy_hash.get_clang_tidy_version_from_requirements", return_value=requirements_version, ), - patch("clang_tidy_hash.extract_platformio_flags", return_value=pio_flags), ): + # Set up mock to return different content based on the file being read + def read_file_mock(path: Path) -> bytes: + if ".clang-tidy" in str(path): + return clang_tidy_content + elif "platformio.ini" in str(path): + return platformio_content + return b"" + + mock_read_bytes.side_effect = read_file_mock result = clang_tidy_hash.calculate_clang_tidy_hash() assert result == expected_hash From b959baf3d67136df36ebef2a6f7f59bc0d828f1e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 20:26:54 -1000 Subject: [PATCH 63/90] Add missing clang-tidy NOLINT comments for ArduinoJson v7 in IDF webserver (#9508) --- esphome/components/web_server_idf/web_server_idf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index d2447681f5..734259093e 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -389,10 +389,12 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * #ifdef USE_WEBSERVER_SORTING for (auto &group : ws->sorting_groups_) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson message = json::build_json([group](JsonObject root) { root["name"] = group.second.name; root["sorting_weight"] = group.second.weight; }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // a (very) large number of these should be able to be queued initially without defer // since the only thing in the send buffer at this point is the initial ping/config From 3f492e3b825ec6c2ef318344ebd1134cba682ee8 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:59:20 +1000 Subject: [PATCH 64/90] [core] Don't issue -Wno-volatile for host platform (#9511) --- esphome/writer.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/esphome/writer.py b/esphome/writer.py index ca9e511c19..5438e48570 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -163,7 +163,7 @@ def get_ini_content(): CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags)) # Add extra script for C++ flags - CORE.add_platformio_option("extra_scripts", ["pre:cxx_flags.py"]) + CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"]) content = "[platformio]\n" content += f"description = ESPHome {__version__}\n" @@ -402,14 +402,18 @@ def write_gitignore(): f.write(GITIGNORE_CONTENT) -CXX_FLAGS_SCRIPT = """# Auto-generated ESPHome script for C++ specific compiler flags +CXX_FLAGS_FILE_NAME = "cxx_flags.py" +CXX_FLAGS_FILE_CONTENTS = """# Auto-generated ESPHome script for C++ specific compiler flags Import("env") -# Add C++ specific warning flags -env.Append(CXXFLAGS=["-Wno-volatile"]) +# Add C++ specific flags """ def write_cxx_flags_script() -> None: - path = CORE.relative_build_path("cxx_flags.py") - write_file_if_changed(path, CXX_FLAGS_SCRIPT) + path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME) + contents = CXX_FLAGS_FILE_CONTENTS + if not CORE.is_host: + contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])' + contents += "\n" + write_file_if_changed(path, contents) From d3342d6a1aa5dbd35ba72ff52fd5425f4a89d133 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Jul 2025 19:20:18 +1200 Subject: [PATCH 65/90] [component] Fix ``is_ready`` flag when loop disabled (#9501) Co-authored-by: J. Nick Koston --- esphome/core/component.cpp | 1 + .../loop_test_component/__init__.py | 28 ++++++++++- .../loop_test_component.cpp | 24 +++++++++ .../loop_test_component/loop_test_component.h | 25 ++++++++++ .../fixtures/loop_disable_enable.yaml | 32 ++++++++++++ tests/integration/test_loop_disable_enable.py | 50 +++++++++++++++++++ 6 files changed, 159 insertions(+), 1 deletion(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index b360e1d20b..c47f16b5f7 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -264,6 +264,7 @@ void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std: bool Component::is_failed() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; } bool Component::is_ready() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP || + (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE || (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP; } bool Component::can_proceed() { return true; } diff --git a/tests/integration/fixtures/external_components/loop_test_component/__init__.py b/tests/integration/fixtures/external_components/loop_test_component/__init__.py index b66d4598f4..3f3a40db09 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/__init__.py +++ b/tests/integration/fixtures/external_components/loop_test_component/__init__.py @@ -1,7 +1,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME +from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL CODEOWNERS = ["@esphome/tests"] @@ -10,10 +10,15 @@ LoopTestComponent = loop_test_component_ns.class_("LoopTestComponent", cg.Compon LoopTestISRComponent = loop_test_component_ns.class_( "LoopTestISRComponent", cg.Component ) +LoopTestUpdateComponent = loop_test_component_ns.class_( + "LoopTestUpdateComponent", cg.PollingComponent +) CONF_DISABLE_AFTER = "disable_after" CONF_TEST_REDUNDANT_OPERATIONS = "test_redundant_operations" CONF_ISR_COMPONENTS = "isr_components" +CONF_UPDATE_COMPONENTS = "update_components" +CONF_DISABLE_LOOP_AFTER = "disable_loop_after" COMPONENT_CONFIG_SCHEMA = cv.Schema( { @@ -31,11 +36,23 @@ ISR_COMPONENT_CONFIG_SCHEMA = cv.Schema( } ) +UPDATE_COMPONENT_CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(LoopTestUpdateComponent), + cv.Required(CONF_NAME): cv.string, + cv.Optional(CONF_DISABLE_LOOP_AFTER, default=0): cv.int_, + cv.Optional(CONF_UPDATE_INTERVAL, default="1s"): cv.update_interval, + } +) + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(LoopTestComponent), cv.Required(CONF_COMPONENTS): cv.ensure_list(COMPONENT_CONFIG_SCHEMA), cv.Optional(CONF_ISR_COMPONENTS): cv.ensure_list(ISR_COMPONENT_CONFIG_SCHEMA), + cv.Optional(CONF_UPDATE_COMPONENTS): cv.ensure_list( + UPDATE_COMPONENT_CONFIG_SCHEMA + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -94,3 +111,12 @@ async def to_code(config): var = cg.new_Pvariable(isr_config[CONF_ID]) await cg.register_component(var, isr_config) cg.add(var.set_name(isr_config[CONF_NAME])) + + # Create update test components + for update_config in config.get(CONF_UPDATE_COMPONENTS, []): + var = cg.new_Pvariable(update_config[CONF_ID]) + await cg.register_component(var, update_config) + + cg.add(var.set_name(update_config[CONF_NAME])) + cg.add(var.set_disable_loop_after(update_config[CONF_DISABLE_LOOP_AFTER])) + cg.add(var.set_update_interval(update_config[CONF_UPDATE_INTERVAL])) diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp index 470740c534..28a05d3d45 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp @@ -39,5 +39,29 @@ void LoopTestComponent::service_disable() { this->disable_loop(); } +// LoopTestUpdateComponent implementation +void LoopTestUpdateComponent::setup() { + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent setup called", this->name_.c_str()); +} + +void LoopTestUpdateComponent::loop() { + this->loop_count_++; + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent loop count: %d", this->name_.c_str(), this->loop_count_); + + // Disable loop after specified count to test component.update when loop is disabled + if (this->disable_loop_after_ > 0 && this->loop_count_ == this->disable_loop_after_) { + ESP_LOGI(TAG, "[%s] Disabling loop after %d iterations", this->name_.c_str(), this->disable_loop_after_); + this->disable_loop(); + } +} + +void LoopTestUpdateComponent::update() { + this->update_count_++; + // Check if loop is disabled by testing component state + bool loop_disabled = this->component_state_ == COMPONENT_STATE_LOOP_DONE; + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent update() called, count: %d, loop_disabled: %s", this->name_.c_str(), + this->update_count_, loop_disabled ? "YES" : "NO"); +} + } // namespace loop_test_component } // namespace esphome diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h index 5c43dd4b43..cdc04d491b 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h @@ -4,6 +4,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/automation.h" +#include "esphome/core/helpers.h" namespace esphome { namespace loop_test_component { @@ -54,5 +55,29 @@ template class DisableAction : public Action { LoopTestComponent *parent_; }; +// Component with update() method to test component.update action +class LoopTestUpdateComponent : public PollingComponent { + public: + LoopTestUpdateComponent() : PollingComponent(1000) {} // Default 1s update interval + + void set_name(const std::string &name) { this->name_ = name; } + void set_disable_loop_after(int count) { this->disable_loop_after_ = count; } + + void setup() override; + void loop() override; + void update() override; + + int get_update_count() const { return this->update_count_; } + int get_loop_count() const { return this->loop_count_; } + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + std::string name_; + int loop_count_{0}; + int update_count_{0}; + int disable_loop_after_{0}; +}; + } // namespace loop_test_component } // namespace esphome diff --git a/tests/integration/fixtures/loop_disable_enable.yaml b/tests/integration/fixtures/loop_disable_enable.yaml index f19d7f60ca..f87fe9130b 100644 --- a/tests/integration/fixtures/loop_disable_enable.yaml +++ b/tests/integration/fixtures/loop_disable_enable.yaml @@ -40,6 +40,13 @@ loop_test_component: - id: isr_test name: "isr_test" + # Update test component to test component.update when loop is disabled + update_components: + - id: update_test_component + name: "update_test" + disable_loop_after: 3 # Disable loop after 3 iterations + update_interval: 0.1s # Fast update interval for testing + # Interval to re-enable the self_disable_10 component after some time interval: - interval: 0.5s @@ -51,3 +58,28 @@ interval: - logger.log: "Re-enabling self_disable_10 via service" - loop_test_component.enable: id: self_disable_10 + + # Test component.update on a component with disabled loop + - interval: 0.1s + then: + - lambda: |- + static bool manual_update_done = false; + if (!manual_update_done && + id(update_test_component).get_loop_count() == 3 && + id(update_test_component).get_update_count() >= 3) { + ESP_LOGI("main", "Manually calling component.update on update_test_component with disabled loop"); + manual_update_done = true; + } + - if: + condition: + lambda: |- + static bool manual_update_triggered = false; + if (!manual_update_triggered && + id(update_test_component).get_loop_count() == 3 && + id(update_test_component).get_update_count() >= 3) { + manual_update_triggered = true; + return true; + } + return false; + then: + - component.update: update_test_component diff --git a/tests/integration/test_loop_disable_enable.py b/tests/integration/test_loop_disable_enable.py index d5f868aa93..e93fc32178 100644 --- a/tests/integration/test_loop_disable_enable.py +++ b/tests/integration/test_loop_disable_enable.py @@ -45,11 +45,18 @@ async def test_loop_disable_enable( isr_component_disabled = asyncio.Event() isr_component_re_enabled = asyncio.Event() isr_component_pure_re_enabled = asyncio.Event() + # Events for update component testing + update_component_loop_disabled = asyncio.Event() + update_component_manual_update_called = asyncio.Event() # Track loop counts for components self_disable_10_counts: list[int] = [] normal_component_counts: list[int] = [] isr_component_counts: list[int] = [] + # Track update component behavior + update_component_loop_count = 0 + update_component_update_count = 0 + update_component_manual_update_count = 0 def on_log_line(line: str) -> None: """Process each log line from the process output.""" @@ -59,6 +66,7 @@ async def test_loop_disable_enable( if ( "loop_test_component" not in clean_line and "loop_test_isr_component" not in clean_line + and "Manually calling component.update" not in clean_line ): return @@ -112,6 +120,23 @@ async def test_loop_disable_enable( elif "Running after pure ISR re-enable!" in clean_line: isr_component_pure_re_enabled.set() + # Update component events + elif "[update_test]" in clean_line: + if "LoopTestUpdateComponent loop count:" in clean_line: + nonlocal update_component_loop_count + update_component_loop_count = int( + clean_line.split("LoopTestUpdateComponent loop count: ")[1] + ) + elif "LoopTestUpdateComponent update() called" in clean_line: + nonlocal update_component_update_count + update_component_update_count += 1 + if "Manually calling component.update" in " ".join(log_messages[-5:]): + nonlocal update_component_manual_update_count + update_component_manual_update_count += 1 + update_component_manual_update_called.set() + elif "Disabling loop after" in clean_line: + update_component_loop_disabled.set() + # Write, compile and run the ESPHome device with log callback async with ( run_compiled(yaml_config, line_callback=on_log_line), @@ -205,3 +230,28 @@ async def test_loop_disable_enable( assert final_count > 10, ( f"Component didn't run after pure ISR enable: got {final_count} counts total" ) + + # Test component.update functionality when loop is disabled + # Wait for update component to disable its loop + try: + await asyncio.wait_for(update_component_loop_disabled.wait(), timeout=3.0) + except asyncio.TimeoutError: + pytest.fail("Update component did not disable its loop within 3 seconds") + + # Verify it ran exactly 3 loops before disabling + assert update_component_loop_count == 3, ( + f"Expected 3 loop iterations before disable, got {update_component_loop_count}" + ) + + # Wait for manual component.update to be called + try: + await asyncio.wait_for( + update_component_manual_update_called.wait(), timeout=5.0 + ) + except asyncio.TimeoutError: + pytest.fail("Manual component.update was not called within 5 seconds") + + # The key test: verify that manual component.update worked after loop was disabled + assert update_component_manual_update_count >= 1, ( + "component.update did not fire after loop was disabled" + ) From e599ab1a03261f6315e95faab8e8d09d8541028a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:55:55 +1200 Subject: [PATCH 66/90] Enable issue tracking (#9515) --- .github/ISSUE_TEMPLATE/bug_report.yml | 92 +++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 15 +++-- 2 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..44722ec85c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,92 @@ +name: Report an issue with ESPHome +description: Report an issue with ESPHome. +body: + - type: markdown + attributes: + value: | + This issue form is for reporting bugs only! + + If you have a feature request or enhancement, please [request them here instead][fr]. + + [fr]: https://github.com/orgs/esphome/discussions + - type: textarea + validations: + required: true + id: problem + attributes: + label: The problem + description: >- + Describe the issue you are experiencing here to communicate to the + maintainers. Tell us what you were trying to do and what happened. + + Provide a clear and concise description of what the problem is. + + - type: markdown + attributes: + value: | + ## Environment + - type: input + id: version + validations: + required: true + attributes: + label: Which version of ESPHome has the issue? + description: > + ESPHome version like 1.19, 2025.6.0 or 2025.XX.X-dev. + - type: dropdown + validations: + required: true + id: installation + attributes: + label: What type of installation are you using? + options: + - Home Assistant Add-on + - Docker + - pip + - type: dropdown + validations: + required: true + id: platform + attributes: + label: What platform are you using? + options: + - ESP8266 + - ESP32 + - RP2040 + - BK72XX + - RTL87XX + - LN882X + - Host + - Other + - type: input + id: component_name + attributes: + label: Component causing the issue + description: > + The name of the component or platform. For example, api/i2c or ultrasonic. + + - type: markdown + attributes: + value: | + # Details + - type: textarea + id: config + attributes: + label: YAML Config + description: | + Include a complete YAML configuration file demonstrating the problem here. Preferably post the *entire* file - don't make assumptions about what is unimportant. However, if it's a large or complicated config then you will need to reduce it to the smallest possible file *that still demonstrates the problem*. If you don't provide enough information to *easily* reproduce the problem, it's unlikely your bug report will get any attention. Logs do not belong here, attach them below. + render: yaml + - type: textarea + id: logs + attributes: + label: Anything in the logs that might be useful for us? + description: For example, error message, or stack traces. Serial or USB logs are much more useful than WiFi logs. + render: txt + - type: textarea + id: additional + attributes: + label: Additional information + description: > + If you have any additional information for us, use the field below. + Please note, you can attach screenshots or screen recordings here, by + dragging and dropping files in the field below. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 804dad47c7..eabfd000f5 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,16 @@ --- blank_issues_enabled: false contact_links: - - name: Issue Tracker - url: https://github.com/esphome/issues - about: Please create bug reports in the dedicated issue tracker. - - name: Feature Request Tracker - url: https://github.com/esphome/feature-requests + - name: Report an issue with the ESPHome documentation + url: https://github.com/esphome/esphome-docs/issues/new/choose + - name: Report an issue with the ESPHome web server + url: https://github.com/esphome/esphome-webserver/issues/new/choose + - name: Report an issue with the ESPHome Builder / Dashboard + url: https://github.com/esphome/dashboard/issues/new/choose + - name: Report an issue with the ESPHome API client + url: https://github.com/esphome/aioesphomeapi/issues/new/choose + - name: Make a Feature Request + url: https://github.com/orgs/esphome/discussions about: | Please create feature requests in the dedicated feature request tracker. - name: Frequently Asked Question From a896190de5d3c09fa2d9dc1e7cd3cddffe9610f0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Jul 2025 22:13:18 +1200 Subject: [PATCH 67/90] [repo] Fix issue template config.yml (#9516) --- .github/ISSUE_TEMPLATE/config.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index eabfd000f5..19f52349a6 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,18 +3,19 @@ blank_issues_enabled: false contact_links: - name: Report an issue with the ESPHome documentation url: https://github.com/esphome/esphome-docs/issues/new/choose + about: Report an issue with the ESPHome documentation. - name: Report an issue with the ESPHome web server url: https://github.com/esphome/esphome-webserver/issues/new/choose + about: Report an issue with the ESPHome web server. - name: Report an issue with the ESPHome Builder / Dashboard url: https://github.com/esphome/dashboard/issues/new/choose + about: Report an issue with the ESPHome Builder / Dashboard. - name: Report an issue with the ESPHome API client url: https://github.com/esphome/aioesphomeapi/issues/new/choose + about: Report an issue with the ESPHome API client. - name: Make a Feature Request url: https://github.com/orgs/esphome/discussions - about: | - Please create feature requests in the dedicated feature request tracker. + about: Please create feature requests in the dedicated feature request tracker. - name: Frequently Asked Question url: https://esphome.io/guides/faq.html - about: | - Please view the FAQ for common questions and what - to include in a bug report. + about: Please view the FAQ for common questions and what to include in a bug report. From 84fc6ff71a7f7417b3486725702874b79b56100d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 13 Jul 2025 21:47:52 -1000 Subject: [PATCH 68/90] Suppress spurious volatile and Python syntax warnings during builds (#9488) --- esphome/platformio_api.py | 2 ++ esphome/writer.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 808db03231..e34ac028f8 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -78,6 +78,8 @@ def run_platformio_cli(*args, **kwargs) -> str | int: os.environ.setdefault( "PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path()) ) + # Suppress Python syntax warnings from third-party scripts during compilation + os.environ.setdefault("PYTHONWARNINGS", "ignore::SyntaxWarning") cmd = ["platformio"] + list(args) if not CORE.verbose: diff --git a/esphome/writer.py b/esphome/writer.py index 943dfa78cc..ca9e511c19 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -162,6 +162,9 @@ def get_ini_content(): # Sort to avoid changing build unflags order CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags)) + # Add extra script for C++ flags + CORE.add_platformio_option("extra_scripts", ["pre:cxx_flags.py"]) + content = "[platformio]\n" content += f"description = ESPHome {__version__}\n" @@ -222,6 +225,9 @@ def write_platformio_project(): write_gitignore() write_platformio_ini(content) + # Write extra script for C++ specific flags + write_cxx_flags_script() + DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\ #pragma once @@ -394,3 +400,16 @@ def write_gitignore(): if not os.path.isfile(path): with open(file=path, mode="w", encoding="utf-8") as f: f.write(GITIGNORE_CONTENT) + + +CXX_FLAGS_SCRIPT = """# Auto-generated ESPHome script for C++ specific compiler flags +Import("env") + +# Add C++ specific warning flags +env.Append(CXXFLAGS=["-Wno-volatile"]) +""" + + +def write_cxx_flags_script() -> None: + path = CORE.relative_build_path("cxx_flags.py") + write_file_if_changed(path, CXX_FLAGS_SCRIPT) From 78e8001aa8c30415b6a285132ad30f1dbf00347f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 12:09:18 +1000 Subject: [PATCH 69/90] [online_image] Support `byte_order` (#9502) --- esphome/components/online_image/__init__.py | 5 ++++- esphome/components/online_image/online_image.cpp | 16 +++++++++++----- esphome/components/online_image/online_image.h | 7 ++++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index 3f15db6e50..7a6d25bc7d 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -2,7 +2,7 @@ import logging from esphome import automation import esphome.codegen as cg -from esphome.components.const import CONF_REQUEST_HEADERS +from esphome.components.const import CONF_BYTE_ORDER, CONF_REQUEST_HEADERS from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( CONF_INVERT_ALPHA, @@ -11,6 +11,7 @@ from esphome.components.image import ( Image_, get_image_type_enum, get_transparency_enum, + validate_settings, ) import esphome.config_validation as cv from esphome.const import ( @@ -161,6 +162,7 @@ CONFIG_SCHEMA = cv.Schema( rp2040_arduino=cv.Version(0, 0, 0), host=cv.Version(0, 0, 0), ), + validate_settings, ) ) @@ -213,6 +215,7 @@ async def to_code(config): get_image_type_enum(config[CONF_TYPE]), transparent, config[CONF_BUFFER_SIZE], + config.get(CONF_BYTE_ORDER) != "LITTLE_ENDIAN", ) await cg.register_component(var, config) await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index d0c743ef93..4e2ecc2c77 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -35,14 +35,15 @@ inline bool is_color_on(const Color &color) { } OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type, - image::Transparency transparency, uint32_t download_buffer_size) + image::Transparency transparency, uint32_t download_buffer_size, bool is_big_endian) : Image(nullptr, 0, 0, type, transparency), buffer_(nullptr), download_buffer_(download_buffer_size), download_buffer_initial_size_(download_buffer_size), format_(format), fixed_width_(width), - fixed_height_(height) { + fixed_height_(height), + is_big_endian_(is_big_endian) { this->set_url(url); } @@ -296,7 +297,7 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { break; } case ImageType::IMAGE_TYPE_GRAYSCALE: { - uint8_t gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); + auto gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) { if (gray == 1) { gray = 0; @@ -314,8 +315,13 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { case ImageType::IMAGE_TYPE_RGB565: { this->map_chroma_key(color); uint16_t col565 = display::ColorUtil::color_to_565(color); - this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); - this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + if (this->is_big_endian_) { + this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); + this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + } else { + this->buffer_[pos + 0] = static_cast(col565 & 0xFF); + this->buffer_[pos + 1] = static_cast((col565 >> 8) & 0xFF); + } if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { this->buffer_[pos + 2] = color.w; } diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 6a2144538f..3326cbe8d6 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -50,7 +50,7 @@ class OnlineImage : public PollingComponent, * @param buffer_size Size of the buffer used to download the image. */ OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type, - image::Transparency transparency, uint32_t buffer_size); + image::Transparency transparency, uint32_t buffer_size, bool is_big_endian); void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; @@ -164,6 +164,11 @@ class OnlineImage : public PollingComponent, const int fixed_width_; /** height requested on configuration, or 0 if non specified. */ const int fixed_height_; + /** + * Whether the image is stored in big-endian format. + * This is used to determine how to store 16 bit colors in the buffer. + */ + bool is_big_endian_; /** * Actual width of the current image. If fixed_width_ is specified, * this will be equal to it; otherwise it will be set once the decoding From 35b3f75f7c954ffb9843aca49de39b27378dedda Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 15 Jul 2025 03:11:10 +0100 Subject: [PATCH 70/90] [json] Bump ArduinoJson library to 7.4.2 (#8857) Co-authored-by: J. Nick Koston Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../update/http_request_update.cpp | 14 +-- esphome/components/json/__init__.py | 2 +- esphome/components/json/json_util.cpp | 119 +++++++++--------- .../components/light/light_json_schema.cpp | 33 ++--- .../mqtt/mqtt_alarm_control_panel.cpp | 3 +- .../components/mqtt/mqtt_binary_sensor.cpp | 1 + esphome/components/mqtt/mqtt_button.cpp | 5 +- esphome/components/mqtt/mqtt_client.cpp | 2 + esphome/components/mqtt/mqtt_climate.cpp | 10 +- esphome/components/mqtt/mqtt_component.cpp | 4 +- esphome/components/mqtt/mqtt_cover.cpp | 1 + esphome/components/mqtt/mqtt_date.cpp | 7 +- esphome/components/mqtt/mqtt_datetime.cpp | 13 +- esphome/components/mqtt/mqtt_event.cpp | 9 +- esphome/components/mqtt/mqtt_fan.cpp | 1 + esphome/components/mqtt/mqtt_light.cpp | 12 +- esphome/components/mqtt/mqtt_lock.cpp | 4 +- esphome/components/mqtt/mqtt_number.cpp | 1 + esphome/components/mqtt/mqtt_select.cpp | 3 +- esphome/components/mqtt/mqtt_sensor.cpp | 4 +- esphome/components/mqtt/mqtt_switch.cpp | 4 +- esphome/components/mqtt/mqtt_text.cpp | 1 + esphome/components/mqtt/mqtt_text_sensor.cpp | 4 +- esphome/components/mqtt/mqtt_time.cpp | 7 +- esphome/components/mqtt/mqtt_update.cpp | 1 + esphome/components/mqtt/mqtt_valve.cpp | 4 +- esphome/components/web_server/web_server.cpp | 22 ++-- platformio.ini | 2 +- 28 files changed, 164 insertions(+), 129 deletions(-) diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 202c7b88b2..06aa6da6a4 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -83,7 +83,7 @@ void HttpRequestUpdate::update_task(void *params) { container.reset(); // Release ownership of the container's shared_ptr valid = json::parse_json(response, [this_update](JsonObject root) -> bool { - if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { + if (!root["name"].is() || !root["version"].is() || !root["builds"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } @@ -91,26 +91,26 @@ void HttpRequestUpdate::update_task(void *params) { this_update->update_info_.latest_version = root["version"].as(); for (auto build : root["builds"].as()) { - if (!build.containsKey("chipFamily")) { + if (!build["chipFamily"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } if (build["chipFamily"] == ESPHOME_VARIANT) { - if (!build.containsKey("ota")) { + if (!build["ota"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - auto ota = build["ota"]; - if (!ota.containsKey("path") || !ota.containsKey("md5")) { + JsonObject ota = build["ota"].as(); + if (!ota["path"].is() || !ota["md5"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } this_update->update_info_.firmware_url = ota["path"].as(); this_update->update_info_.md5 = ota["md5"].as(); - if (ota.containsKey("summary")) + if (ota["summary"].is()) this_update->update_info_.summary = ota["summary"].as(); - if (ota.containsKey("release_url")) + if (ota["release_url"].is()) this_update->update_info_.release_url = ota["release_url"].as(); return true; diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 6a0e4c50d2..9773bf67ce 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -12,6 +12,6 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(1.0) async def to_code(config): - cg.add_library("bblanchon/ArduinoJson", "6.18.5") + cg.add_library("bblanchon/ArduinoJson", "7.4.2") cg.add_define("USE_JSON") cg.add_global(json_ns.using) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 6c66476dc1..94c531222a 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,83 +1,76 @@ #include "json_util.h" #include "esphome/core/log.h" +// ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h + namespace esphome { namespace json { static const char *const TAG = "json"; -static std::vector global_json_build_buffer; // NOLINT -static const auto ALLOCATOR = RAMAllocator(RAMAllocator::ALLOC_INTERNAL); +// Build an allocator for the JSON Library using the RAMAllocator class +struct SpiRamAllocator : ArduinoJson::Allocator { + void *allocate(size_t size) override { return this->allocator_.allocate(size); } + + void deallocate(void *pointer) override { + // ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate. + // RAMAllocator::deallocate() requires the size, which we don't have access to here. + // RAMAllocator::deallocate implementation just calls free() regardless of whether + // the memory was allocated with heap_caps_malloc or malloc. + // This is safe because ESP-IDF's heap implementation internally tracks the memory region + // and routes free() to the appropriate heap. + free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) + } + + void *reallocate(void *ptr, size_t new_size) override { + return this->allocator_.reallocate(static_cast(ptr), new_size); + } + + protected: + RAMAllocator allocator_{RAMAllocator(RAMAllocator::NONE)}; +}; std::string build_json(const json_build_t &f) { - // Here we are allocating up to 5kb of memory, - // with the heap size minus 2kb to be safe if less than 5kb - // as we can not have a true dynamic sized document. - // The excess memory is freed below with `shrinkToFit()` - auto free_heap = ALLOCATOR.get_max_free_block_size(); - size_t request_size = std::min(free_heap, (size_t) 512); - while (true) { - ESP_LOGV(TAG, "Attempting to allocate %zu bytes for JSON serialization", request_size); - DynamicJsonDocument json_document(request_size); - if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, largest free heap block: %zu bytes", - request_size, free_heap); - return "{}"; - } - JsonObject root = json_document.to(); - f(root); - if (json_document.overflowed()) { - if (request_size == free_heap) { - ESP_LOGE(TAG, "Could not allocate memory for document! Overflowed largest free heap block: %zu bytes", - free_heap); - return "{}"; - } - request_size = std::min(request_size * 2, free_heap); - continue; - } - json_document.shrinkToFit(); - ESP_LOGV(TAG, "Size after shrink %zu bytes", json_document.capacity()); - std::string output; - serializeJson(json_document, output); - return output; + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + auto doc_allocator = SpiRamAllocator(); + JsonDocument json_document(&doc_allocator); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return "{}"; } + JsonObject root = json_document.to(); + f(root); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return "{}"; + } + std::string output; + serializeJson(json_document, output); + return output; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } bool parse_json(const std::string &data, const json_parse_t &f) { - // Here we are allocating 1.5 times the data size, - // with the heap size minus 2kb to be safe if less than that - // as we can not have a true dynamic sized document. - // The excess memory is freed below with `shrinkToFit()` - auto free_heap = ALLOCATOR.get_max_free_block_size(); - size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); - while (true) { - DynamicJsonDocument json_document(request_size); - if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, free heap: %zu", request_size, - free_heap); - return false; - } - DeserializationError err = deserializeJson(json_document, data); - json_document.shrinkToFit(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + auto doc_allocator = SpiRamAllocator(); + JsonDocument json_document(&doc_allocator); + if (json_document.overflowed()) { + ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); + return false; + } + DeserializationError err = deserializeJson(json_document, data); - JsonObject root = json_document.as(); + JsonObject root = json_document.as(); - if (err == DeserializationError::Ok) { - return f(root); - } else if (err == DeserializationError::NoMemory) { - if (request_size * 2 >= free_heap) { - ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); - return false; - } - ESP_LOGV(TAG, "Increasing memory allocation."); - request_size *= 2; - continue; - } else { - ESP_LOGE(TAG, "Parse error: %s", err.c_str()); - return false; - } - }; + if (err == DeserializationError::Ok) { + return f(root); + } else if (err == DeserializationError::NoMemory) { + ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller"); + return false; + } + ESP_LOGE(TAG, "Parse error: %s", err.c_str()); return false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } } // namespace json diff --git a/esphome/components/light/light_json_schema.cpp b/esphome/components/light/light_json_schema.cpp index 6f8cc11f25..26615bae5c 100644 --- a/esphome/components/light/light_json_schema.cpp +++ b/esphome/components/light/light_json_schema.cpp @@ -9,6 +9,7 @@ namespace light { // See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema void LightJSONSchema::dump_json(LightState &state, JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (state.supports_effects()) root["effect"] = state.get_effect_name(); @@ -52,7 +53,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { if (values.get_color_mode() & ColorCapability::BRIGHTNESS) root["brightness"] = uint8_t(values.get_brightness() * 255); - JsonObject color = root.createNestedObject("color"); + JsonObject color = root["color"].to(); if (values.get_color_mode() & ColorCapability::RGB) { color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255); color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255); @@ -73,7 +74,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) { } void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject root) { - if (root.containsKey("state")) { + if (root["state"].is()) { auto val = parse_on_off(root["state"]); switch (val) { case PARSE_ON: @@ -90,40 +91,40 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO } } - if (root.containsKey("brightness")) { + if (root["brightness"].is()) { call.set_brightness(float(root["brightness"]) / 255.0f); } - if (root.containsKey("color")) { + if (root["color"].is()) { JsonObject color = root["color"]; // HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness. float max_rgb = 0.0f; - if (color.containsKey("r")) { + if (color["r"].is()) { float r = float(color["r"]) / 255.0f; max_rgb = fmaxf(max_rgb, r); call.set_red(r); } - if (color.containsKey("g")) { + if (color["g"].is()) { float g = float(color["g"]) / 255.0f; max_rgb = fmaxf(max_rgb, g); call.set_green(g); } - if (color.containsKey("b")) { + if (color["b"].is()) { float b = float(color["b"]) / 255.0f; max_rgb = fmaxf(max_rgb, b); call.set_blue(b); } - if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) { + if (color["r"].is() || color["g"].is() || color["b"].is()) { call.set_color_brightness(max_rgb); } - if (color.containsKey("c")) { + if (color["c"].is()) { call.set_cold_white(float(color["c"]) / 255.0f); } - if (color.containsKey("w")) { + if (color["w"].is()) { // the HA scheme is ambiguous here, the same key is used for white channel in RGBW and warm // white channel in RGBWW. - if (color.containsKey("c")) { + if (color["c"].is()) { call.set_warm_white(float(color["w"]) / 255.0f); } else { call.set_white(float(color["w"]) / 255.0f); @@ -131,11 +132,11 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO } } - if (root.containsKey("white_value")) { // legacy API + if (root["white_value"].is()) { // legacy API call.set_white(float(root["white_value"]) / 255.0f); } - if (root.containsKey("color_temp")) { + if (root["color_temp"].is()) { call.set_color_temperature(float(root["color_temp"])); } } @@ -143,17 +144,17 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject root) { LightJSONSchema::parse_color_json(state, call, root); - if (root.containsKey("flash")) { + if (root["flash"].is()) { auto length = uint32_t(float(root["flash"]) * 1000); call.set_flash_length(length); } - if (root.containsKey("transition")) { + if (root["transition"].is()) { auto length = uint32_t(float(root["transition"]) * 1000); call.set_transition_length(length); } - if (root.containsKey("effect")) { + if (root["effect"].is()) { const char *effect = root["effect"]; call.set_effect(effect); } diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 0a38598679..94460c31a7 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -55,7 +55,8 @@ void MQTTAlarmControlPanelComponent::dump_config() { } void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to(); const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features(); if (acp_supported_features & ACP_FEAT_ARM_AWAY) { supported_features.add("arm_away"); diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 6d12e88391..2ce4928574 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -30,6 +30,7 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor } void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (!this->binary_sensor_->get_device_class().empty()) root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class(); if (this->binary_sensor_->is_status_binary_sensor()) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index 204f60fe67..c619a02344 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -31,9 +31,12 @@ void MQTTButtonComponent::dump_config() { } void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; - if (!this->button_->get_device_class().empty()) + if (!this->button_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } std::string MQTTButtonComponent::component_type() const { return "button"; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index ab7fd15a35..5b93789447 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -92,6 +92,7 @@ void MQTTClientComponent::send_device_info_() { std::string topic = "esphome/discover/"; topic.append(App.get_name()); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson this->publish_json( topic, [](JsonObject root) { @@ -147,6 +148,7 @@ void MQTTClientComponent::send_device_info_() { #endif }, 2, this->discovery_info_.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClientComponent::dump_config() { diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index a8768114a4..e16f097812 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -14,6 +14,7 @@ static const char *const TAG = "mqtt.climate"; using namespace esphome::climate; void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto traits = this->device_->get_traits(); // current_temperature_topic if (traits.get_supports_current_temperature()) { @@ -28,7 +29,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // mode_state_topic root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic(); // modes - JsonArray modes = root.createNestedArray(MQTT_MODES); + JsonArray modes = root[MQTT_MODES].to(); // sort array for nice UI in HA if (traits.supports_mode(CLIMATE_MODE_AUTO)) modes.add("auto"); @@ -89,7 +90,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // preset_mode_state_topic root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic(); // presets - JsonArray presets = root.createNestedArray("preset_modes"); + JsonArray presets = root["preset_modes"].to(); if (traits.supports_preset(CLIMATE_PRESET_HOME)) presets.add("home"); if (traits.supports_preset(CLIMATE_PRESET_AWAY)) @@ -119,7 +120,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // fan_mode_state_topic root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic(); // fan_modes - JsonArray fan_modes = root.createNestedArray("fan_modes"); + JsonArray fan_modes = root["fan_modes"].to(); if (traits.supports_fan_mode(CLIMATE_FAN_ON)) fan_modes.add("on"); if (traits.supports_fan_mode(CLIMATE_FAN_OFF)) @@ -150,7 +151,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // swing_mode_state_topic root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic(); // swing_modes - JsonArray swing_modes = root.createNestedArray("swing_modes"); + JsonArray swing_modes = root["swing_modes"].to(); if (traits.supports_swing_mode(CLIMATE_SWING_OFF)) swing_modes.add("off"); if (traits.supports_swing_mode(CLIMATE_SWING_BOTH)) @@ -163,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo config.state_topic = false; config.command_topic = false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClimateComponent::setup() { auto traits = this->device_->get_traits(); diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index eee5644c9d..b51f4d903e 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -70,6 +70,7 @@ bool MQTTComponent::send_discovery_() { ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), [this](JsonObject root) { @@ -155,7 +156,7 @@ bool MQTTComponent::send_discovery_() { } std::string node_area = App.get_area(); - JsonObject device_info = root.createNestedObject(MQTT_DEVICE); + JsonObject device_info = root[MQTT_DEVICE].to(); const auto mac = get_mac_address(); device_info[MQTT_DEVICE_IDENTIFIERS] = mac; device_info[MQTT_DEVICE_NAME] = node_friendly_name; @@ -192,6 +193,7 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac; }, this->qos_, discovery_info.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } uint8_t MQTTComponent::get_qos() const { return this->qos_; } diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 8d09d836f3..6fb61ee469 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -67,6 +67,7 @@ void MQTTCoverComponent::dump_config() { } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (!this->cover_->get_device_class().empty()) root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class(); diff --git a/esphome/components/mqtt/mqtt_date.cpp b/esphome/components/mqtt/mqtt_date.cpp index 088a4788ed..0f0a334ae7 100644 --- a/esphome/components/mqtt/mqtt_date.cpp +++ b/esphome/components/mqtt/mqtt_date.cpp @@ -20,13 +20,13 @@ MQTTDateComponent::MQTTDateComponent(DateEntity *date) : date_(date) {} void MQTTDateComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->date_->make_call(); - if (root.containsKey("year")) { + if (root["year"].is()) { call.set_year(root["year"]); } - if (root.containsKey("month")) { + if (root["month"].is()) { call.set_month(root["month"]); } - if (root.containsKey("day")) { + if (root["day"].is()) { call.set_day(root["day"]); } call.perform(); @@ -55,6 +55,7 @@ bool MQTTDateComponent::send_initial_state() { } bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) { return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["year"] = year; root["month"] = month; root["day"] = day; diff --git a/esphome/components/mqtt/mqtt_datetime.cpp b/esphome/components/mqtt/mqtt_datetime.cpp index 4ae6d0d416..5c56baabe0 100644 --- a/esphome/components/mqtt/mqtt_datetime.cpp +++ b/esphome/components/mqtt/mqtt_datetime.cpp @@ -20,22 +20,22 @@ MQTTDateTimeComponent::MQTTDateTimeComponent(DateTimeEntity *datetime) : datetim void MQTTDateTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->datetime_->make_call(); - if (root.containsKey("year")) { + if (root["year"].is()) { call.set_year(root["year"]); } - if (root.containsKey("month")) { + if (root["month"].is()) { call.set_month(root["month"]); } - if (root.containsKey("day")) { + if (root["day"].is()) { call.set_day(root["day"]); } - if (root.containsKey("hour")) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root.containsKey("minute")) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root.containsKey("second")) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform(); @@ -68,6 +68,7 @@ bool MQTTDateTimeComponent::send_initial_state() { bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["year"] = year; root["month"] = month; root["day"] = day; diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index cf0b90e3d6..f972d545c6 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -16,7 +16,8 @@ using namespace esphome::event; MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {} void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray event_types = root[MQTT_EVENT_TYPES].to(); for (const auto &event_type : this->event_->get_event_types()) event_types.add(event_type); @@ -40,8 +41,10 @@ void MQTTEventComponent::dump_config() { } bool MQTTEventComponent::publish_event_(const std::string &event_type) { - return this->publish_json(this->get_state_topic_(), - [event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; }); + return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + root[MQTT_EVENT_TYPE] = event_type; + }); } std::string MQTTEventComponent::component_type() const { return "event"; } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 35713bdab6..70e1ae3b4a 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -143,6 +143,7 @@ void MQTTFanComponent::dump_config() { bool MQTTFanComponent::send_initial_state() { return this->publish_state(); } void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (this->state_->get_traits().supports_direction()) { root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic(); root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic(); diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index f970da7d8c..4f5ff408a4 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -32,17 +32,21 @@ void MQTTJSONLightComponent::setup() { MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {} bool MQTTJSONLightComponent::publish_state_() { - return this->publish_json(this->get_state_topic_(), - [this](JsonObject root) { LightJSONSchema::dump_json(*this->state_, root); }); + return this->publish_json(this->get_state_topic_(), [this](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + LightJSONSchema::dump_json(*this->state_, root); + }); } LightState *MQTTJSONLightComponent::get_state() const { return this->state_; } void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["schema"] = "json"; auto traits = this->state_->get_traits(); root[MQTT_COLOR_MODE] = true; - JsonArray color_modes = root.createNestedArray("supported_color_modes"); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray color_modes = root["supported_color_modes"].to(); if (traits.supports_color_mode(ColorMode::ON_OFF)) color_modes.add("onoff"); if (traits.supports_color_mode(ColorMode::BRIGHTNESS)) @@ -67,7 +71,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery if (this->state_->supports_effects()) { root["effect"] = true; - JsonArray effect_list = root.createNestedArray(MQTT_EFFECT_LIST); + JsonArray effect_list = root[MQTT_EFFECT_LIST].to(); for (auto *effect : this->state_->get_effects()) effect_list.add(effect->get_name()); effect_list.add("None"); diff --git a/esphome/components/mqtt/mqtt_lock.cpp b/esphome/components/mqtt/mqtt_lock.cpp index f4a5126d0c..0412624983 100644 --- a/esphome/components/mqtt/mqtt_lock.cpp +++ b/esphome/components/mqtt/mqtt_lock.cpp @@ -38,8 +38,10 @@ void MQTTLockComponent::dump_config() { std::string MQTTLockComponent::component_type() const { return "lock"; } const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; } void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (this->lock_->traits.get_assumed_state()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (this->lock_->traits.get_assumed_state()) { root[MQTT_OPTIMISTIC] = true; + } if (this->lock_->traits.get_supports_open()) root[MQTT_PAYLOAD_OPEN] = "OPEN"; } diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index 3a6ea97967..a44632ff30 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -40,6 +40,7 @@ const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { const auto &traits = number_->traits; // https://www.home-assistant.io/integrations/number.mqtt/ + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MAX] = traits.get_max_value(); root[MQTT_STEP] = traits.get_step(); diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index ea5130f823..b851348306 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -35,7 +35,8 @@ const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_ void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { const auto &traits = select_->traits; // https://www.home-assistant.io/integrations/select.mqtt/ - JsonArray options = root.createNestedArray(MQTT_OPTIONS); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + JsonArray options = root[MQTT_OPTIONS].to(); for (const auto &option : traits.get_options()) options.add(option); diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 2cbc291ccf..9324ea9bb1 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -44,8 +44,10 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->sensor_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->sensor_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + } if (!this->sensor_->get_unit_of_measurement().empty()) root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement(); diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index 3fd578825a..8b1323bdb2 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -45,8 +45,10 @@ void MQTTSwitchComponent::dump_config() { std::string MQTTSwitchComponent::component_type() const { return "switch"; } const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; } void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (this->switch_->assumed_state()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (this->switch_->assumed_state()) { root[MQTT_OPTIMISTIC] = true; + } } bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); } diff --git a/esphome/components/mqtt/mqtt_text.cpp b/esphome/components/mqtt/mqtt_text.cpp index cb852b64cd..5ab0ca9688 100644 --- a/esphome/components/mqtt/mqtt_text.cpp +++ b/esphome/components/mqtt/mqtt_text.cpp @@ -34,6 +34,7 @@ std::string MQTTTextComponent::component_type() const { return "text"; } const EntityBase *MQTTTextComponent::get_entity() const { return this->text_; } void MQTTTextComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson switch (this->text_->traits.get_mode()) { case TEXT_MODE_TEXT: root[MQTT_MODE] = "text"; diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index b0754bc8b3..0cc5de07a3 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -15,8 +15,10 @@ using namespace esphome::text_sensor; MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {} void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->sensor_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->sensor_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + } config.command_topic = false; } void MQTTTextSensor::setup() { diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp index 332ef53cbc..0c95bd8147 100644 --- a/esphome/components/mqtt/mqtt_time.cpp +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {} void MQTTTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->time_->make_call(); - if (root.containsKey("hour")) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root.containsKey("minute")) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root.containsKey("second")) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform(); @@ -55,6 +55,7 @@ bool MQTTTimeComponent::send_initial_state() { } bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) { return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["hour"] = hour; root["minute"] = minute; root["second"] = second; diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 2ed8faf074..5d4807c7f3 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -41,6 +41,7 @@ bool MQTTUpdateComponent::publish_state() { } void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["schema"] = "json"; root[MQTT_PAYLOAD_INSTALL] = "INSTALL"; } diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 85e06fe79c..551398cf42 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -49,8 +49,10 @@ void MQTTValveComponent::dump_config() { } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - if (!this->valve_->get_device_class().empty()) + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + if (!this->valve_->get_device_class().empty()) { root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class(); + } auto traits = this->valve_->get_traits(); if (traits.get_is_assumed_state()) { diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 8ced5b7e18..170947dc20 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -792,7 +792,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi light::LightJSONSchema::dump_json(*obj, root); if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("effects"); + JsonArray opt = root["effects"].to(); opt.add("None"); for (auto const &option : obj->get_effects()) { opt.add(option->get_name()); @@ -1238,7 +1238,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value return json::build_json([this, obj, value, start_config](JsonObject root) { set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config); if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("option"); + JsonArray opt = root["option"].to(); for (auto &option : obj->traits.get_options()) { opt.add(option); } @@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); @@ -1330,32 +1331,32 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf char buf[16]; if (start_config == DETAIL_ALL) { - JsonArray opt = root.createNestedArray("modes"); + JsonArray opt = root["modes"].to(); for (climate::ClimateMode m : traits.get_supported_modes()) opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); if (!traits.get_supported_custom_fan_modes().empty()) { - JsonArray opt = root.createNestedArray("fan_modes"); + JsonArray opt = root["fan_modes"].to(); for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { - JsonArray opt = root.createNestedArray("custom_fan_modes"); + JsonArray opt = root["custom_fan_modes"].to(); for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) opt.add(custom_fan_mode); } if (traits.get_supports_swing_modes()) { - JsonArray opt = root.createNestedArray("swing_modes"); + JsonArray opt = root["swing_modes"].to(); for (auto swing_mode : traits.get_supported_swing_modes()) opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode))); } if (traits.get_supports_presets() && obj->preset.has_value()) { - JsonArray opt = root.createNestedArray("presets"); + JsonArray opt = root["presets"].to(); for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { - JsonArray opt = root.createNestedArray("custom_presets"); + JsonArray opt = root["custom_presets"].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); } @@ -1407,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["state"] = root["target_temperature"]; } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif @@ -1635,7 +1637,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty root["event_type"] = event_type; } if (start_config == DETAIL_ALL) { - JsonArray event_types = root.createNestedArray("event_types"); + JsonArray event_types = root["event_types"].to(); for (auto const &event_type : obj->get_event_types()) { event_types.add(event_type); } @@ -1682,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; @@ -1707,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c this->add_sorting_info_(root, obj); } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif diff --git a/platformio.ini b/platformio.ini index 54c72eb28d..f9e4e31ece 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,7 +35,7 @@ build_flags = lib_deps = esphome/noise-c@0.1.10 ; api improv/Improv@1.2.4 ; improv_serial / esp32_improv - bblanchon/ArduinoJson@6.18.5 ; json + bblanchon/ArduinoJson@7.4.2 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 pavlodn/HaierProtocol@0.9.31 ; haier From 42b6939e90fafcd3cc23792e914aad6907a5bf05 Mon Sep 17 00:00:00 2001 From: skyegecko Date: Tue, 15 Jul 2025 04:15:47 +0200 Subject: [PATCH 71/90] [fan] Do not save state for fan if configured as NO_RESTORE (#9472) --- esphome/components/fan/fan.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 25f710f893..82fc5319e0 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -177,6 +177,10 @@ optional Fan::restore_state_() { return {}; } void Fan::save_state_() { + if (this->restore_mode_ == FanRestoreMode::NO_RESTORE) { + return; + } + FanRestoreState state{}; state.state = this->state; state.oscillating = this->oscillating; From 6148dd7e4113ac2dc1feb301a65bd8ee50ca9921 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 16:49:12 -1000 Subject: [PATCH 72/90] Fix LibreTiny compilation error by updating ESPAsyncWebServer and dependencies (#9492) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/async_tcp/__init__.py | 2 +- esphome/components/web_server_base/__init__.py | 2 +- platformio.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 29097ce1b6..4a469fa0e0 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -31,7 +31,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: # https://github.com/ESP32Async/AsyncTCP - cg.add_library("ESP32Async/AsyncTCP", "3.4.4") + cg.add_library("ESP32Async/AsyncTCP", "3.4.5") elif CORE.is_esp8266: # https://github.com/ESP32Async/ESPAsyncTCP cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0") diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 754bf7d433..9f3371c233 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -40,4 +40,4 @@ async def to_code(config): if CORE.is_esp8266: cg.add_library("ESP8266WiFi", None) # https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json - cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.8") + cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10") diff --git a/platformio.ini b/platformio.ini index f9e4e31ece..8fcc578103 100644 --- a/platformio.ini +++ b/platformio.ini @@ -235,7 +235,7 @@ build_flags = -DUSE_ZEPHYR -DUSE_NRF52 lib_deps = - bblanchon/ArduinoJson@7.0.0 ; json + bblanchon/ArduinoJson@7.4.2 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code pavlodn/HaierProtocol@0.9.31 ; haier functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 From 11a051401f316a743f319384c260c1d35811fbbf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:36:07 +1000 Subject: [PATCH 73/90] [captive_portal] Add test case for libretiny (#9457) Co-authored-by: J. Nick Koston --- tests/components/captive_portal/test.bk72xx-ard.yaml | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/components/captive_portal/test.bk72xx-ard.yaml diff --git a/tests/components/captive_portal/test.bk72xx-ard.yaml b/tests/components/captive_portal/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/captive_portal/test.bk72xx-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 321f2f87b0d251e076a63f6ad82611a7f37a7af8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 14 Jul 2025 22:43:00 -0500 Subject: [PATCH 74/90] [opentherm.output] Fix ``lerp`` (#9506) --- esphome/components/opentherm/output/output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/opentherm/output/output.cpp b/esphome/components/opentherm/output/output.cpp index f820dc76f1..486aa0d4e7 100644 --- a/esphome/components/opentherm/output/output.cpp +++ b/esphome/components/opentherm/output/output.cpp @@ -10,7 +10,7 @@ void opentherm::OpenthermOutput::write_state(float state) { ESP_LOGD(TAG, "Received state: %.2f. Min value: %.2f, max value: %.2f", state, min_value_, max_value_); this->state = state < 0.003 && this->zero_means_zero_ ? 0.0 - : clamp(lerp(state, min_value_, max_value_), min_value_, max_value_); + : clamp(std::lerp(min_value_, max_value_, state), min_value_, max_value_); this->has_state_ = true; ESP_LOGD(TAG, "Output %s set to %.2f", this->id_, this->state); } From 7f01c25782a3ec84f4ef430eaf5576d71369e473 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 14 Jul 2025 22:45:38 -0500 Subject: [PATCH 75/90] [servo] Fix ``lerp`` (#9507) --- esphome/components/servo/servo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index b8546d345c..b4511de2d0 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -88,9 +88,9 @@ void Servo::internal_write(float value) { value = clamp(value, -1.0f, 1.0f); float level; if (value < 0.0) { - level = lerp(-value, this->idle_level_, this->min_level_); + level = std::lerp(this->idle_level_, this->min_level_, -value); } else { - level = lerp(value, this->idle_level_, this->max_level_); + level = std::lerp(this->idle_level_, this->max_level_, value); } this->output_->set_level(level); this->current_value_ = value; From 786cb7ded5a93db8e4e820e3addd1c5530ebb263 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 20:26:54 -1000 Subject: [PATCH 76/90] Add missing clang-tidy NOLINT comments for ArduinoJson v7 in IDF webserver (#9508) --- esphome/components/web_server_idf/web_server_idf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index d2447681f5..734259093e 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -389,10 +389,12 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest * #ifdef USE_WEBSERVER_SORTING for (auto &group : ws->sorting_groups_) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson message = json::build_json([group](JsonObject root) { root["name"] = group.second.name; root["sorting_weight"] = group.second.weight; }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) // a (very) large number of these should be able to be queued initially without defer // since the only thing in the send buffer at this point is the initial ping/config From 9bc3ff5f53270bf7ce67f8f782820f3be128612c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:59:20 +1000 Subject: [PATCH 77/90] [core] Don't issue -Wno-volatile for host platform (#9511) --- esphome/writer.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/esphome/writer.py b/esphome/writer.py index ca9e511c19..5438e48570 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -163,7 +163,7 @@ def get_ini_content(): CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags)) # Add extra script for C++ flags - CORE.add_platformio_option("extra_scripts", ["pre:cxx_flags.py"]) + CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"]) content = "[platformio]\n" content += f"description = ESPHome {__version__}\n" @@ -402,14 +402,18 @@ def write_gitignore(): f.write(GITIGNORE_CONTENT) -CXX_FLAGS_SCRIPT = """# Auto-generated ESPHome script for C++ specific compiler flags +CXX_FLAGS_FILE_NAME = "cxx_flags.py" +CXX_FLAGS_FILE_CONTENTS = """# Auto-generated ESPHome script for C++ specific compiler flags Import("env") -# Add C++ specific warning flags -env.Append(CXXFLAGS=["-Wno-volatile"]) +# Add C++ specific flags """ def write_cxx_flags_script() -> None: - path = CORE.relative_build_path("cxx_flags.py") - write_file_if_changed(path, CXX_FLAGS_SCRIPT) + path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME) + contents = CXX_FLAGS_FILE_CONTENTS + if not CORE.is_host: + contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])' + contents += "\n" + write_file_if_changed(path, contents) From 02b7db73112d73bf5de6d298712c9339ea49dc85 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Jul 2025 19:20:18 +1200 Subject: [PATCH 78/90] [component] Fix ``is_ready`` flag when loop disabled (#9501) Co-authored-by: J. Nick Koston --- esphome/core/component.cpp | 1 + .../loop_test_component/__init__.py | 28 ++++++++++- .../loop_test_component.cpp | 24 +++++++++ .../loop_test_component/loop_test_component.h | 25 ++++++++++ .../fixtures/loop_disable_enable.yaml | 32 ++++++++++++ tests/integration/test_loop_disable_enable.py | 50 +++++++++++++++++++ 6 files changed, 159 insertions(+), 1 deletion(-) diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index b360e1d20b..c47f16b5f7 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -264,6 +264,7 @@ void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std: bool Component::is_failed() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; } bool Component::is_ready() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP || + (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE || (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP; } bool Component::can_proceed() { return true; } diff --git a/tests/integration/fixtures/external_components/loop_test_component/__init__.py b/tests/integration/fixtures/external_components/loop_test_component/__init__.py index b66d4598f4..3f3a40db09 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/__init__.py +++ b/tests/integration/fixtures/external_components/loop_test_component/__init__.py @@ -1,7 +1,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME +from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL CODEOWNERS = ["@esphome/tests"] @@ -10,10 +10,15 @@ LoopTestComponent = loop_test_component_ns.class_("LoopTestComponent", cg.Compon LoopTestISRComponent = loop_test_component_ns.class_( "LoopTestISRComponent", cg.Component ) +LoopTestUpdateComponent = loop_test_component_ns.class_( + "LoopTestUpdateComponent", cg.PollingComponent +) CONF_DISABLE_AFTER = "disable_after" CONF_TEST_REDUNDANT_OPERATIONS = "test_redundant_operations" CONF_ISR_COMPONENTS = "isr_components" +CONF_UPDATE_COMPONENTS = "update_components" +CONF_DISABLE_LOOP_AFTER = "disable_loop_after" COMPONENT_CONFIG_SCHEMA = cv.Schema( { @@ -31,11 +36,23 @@ ISR_COMPONENT_CONFIG_SCHEMA = cv.Schema( } ) +UPDATE_COMPONENT_CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(LoopTestUpdateComponent), + cv.Required(CONF_NAME): cv.string, + cv.Optional(CONF_DISABLE_LOOP_AFTER, default=0): cv.int_, + cv.Optional(CONF_UPDATE_INTERVAL, default="1s"): cv.update_interval, + } +) + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(LoopTestComponent), cv.Required(CONF_COMPONENTS): cv.ensure_list(COMPONENT_CONFIG_SCHEMA), cv.Optional(CONF_ISR_COMPONENTS): cv.ensure_list(ISR_COMPONENT_CONFIG_SCHEMA), + cv.Optional(CONF_UPDATE_COMPONENTS): cv.ensure_list( + UPDATE_COMPONENT_CONFIG_SCHEMA + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -94,3 +111,12 @@ async def to_code(config): var = cg.new_Pvariable(isr_config[CONF_ID]) await cg.register_component(var, isr_config) cg.add(var.set_name(isr_config[CONF_NAME])) + + # Create update test components + for update_config in config.get(CONF_UPDATE_COMPONENTS, []): + var = cg.new_Pvariable(update_config[CONF_ID]) + await cg.register_component(var, update_config) + + cg.add(var.set_name(update_config[CONF_NAME])) + cg.add(var.set_disable_loop_after(update_config[CONF_DISABLE_LOOP_AFTER])) + cg.add(var.set_update_interval(update_config[CONF_UPDATE_INTERVAL])) diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp index 470740c534..28a05d3d45 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.cpp @@ -39,5 +39,29 @@ void LoopTestComponent::service_disable() { this->disable_loop(); } +// LoopTestUpdateComponent implementation +void LoopTestUpdateComponent::setup() { + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent setup called", this->name_.c_str()); +} + +void LoopTestUpdateComponent::loop() { + this->loop_count_++; + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent loop count: %d", this->name_.c_str(), this->loop_count_); + + // Disable loop after specified count to test component.update when loop is disabled + if (this->disable_loop_after_ > 0 && this->loop_count_ == this->disable_loop_after_) { + ESP_LOGI(TAG, "[%s] Disabling loop after %d iterations", this->name_.c_str(), this->disable_loop_after_); + this->disable_loop(); + } +} + +void LoopTestUpdateComponent::update() { + this->update_count_++; + // Check if loop is disabled by testing component state + bool loop_disabled = this->component_state_ == COMPONENT_STATE_LOOP_DONE; + ESP_LOGI(TAG, "[%s] LoopTestUpdateComponent update() called, count: %d, loop_disabled: %s", this->name_.c_str(), + this->update_count_, loop_disabled ? "YES" : "NO"); +} + } // namespace loop_test_component } // namespace esphome diff --git a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h index 5c43dd4b43..cdc04d491b 100644 --- a/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h +++ b/tests/integration/fixtures/external_components/loop_test_component/loop_test_component.h @@ -4,6 +4,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/automation.h" +#include "esphome/core/helpers.h" namespace esphome { namespace loop_test_component { @@ -54,5 +55,29 @@ template class DisableAction : public Action { LoopTestComponent *parent_; }; +// Component with update() method to test component.update action +class LoopTestUpdateComponent : public PollingComponent { + public: + LoopTestUpdateComponent() : PollingComponent(1000) {} // Default 1s update interval + + void set_name(const std::string &name) { this->name_ = name; } + void set_disable_loop_after(int count) { this->disable_loop_after_ = count; } + + void setup() override; + void loop() override; + void update() override; + + int get_update_count() const { return this->update_count_; } + int get_loop_count() const { return this->loop_count_; } + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + std::string name_; + int loop_count_{0}; + int update_count_{0}; + int disable_loop_after_{0}; +}; + } // namespace loop_test_component } // namespace esphome diff --git a/tests/integration/fixtures/loop_disable_enable.yaml b/tests/integration/fixtures/loop_disable_enable.yaml index f19d7f60ca..f87fe9130b 100644 --- a/tests/integration/fixtures/loop_disable_enable.yaml +++ b/tests/integration/fixtures/loop_disable_enable.yaml @@ -40,6 +40,13 @@ loop_test_component: - id: isr_test name: "isr_test" + # Update test component to test component.update when loop is disabled + update_components: + - id: update_test_component + name: "update_test" + disable_loop_after: 3 # Disable loop after 3 iterations + update_interval: 0.1s # Fast update interval for testing + # Interval to re-enable the self_disable_10 component after some time interval: - interval: 0.5s @@ -51,3 +58,28 @@ interval: - logger.log: "Re-enabling self_disable_10 via service" - loop_test_component.enable: id: self_disable_10 + + # Test component.update on a component with disabled loop + - interval: 0.1s + then: + - lambda: |- + static bool manual_update_done = false; + if (!manual_update_done && + id(update_test_component).get_loop_count() == 3 && + id(update_test_component).get_update_count() >= 3) { + ESP_LOGI("main", "Manually calling component.update on update_test_component with disabled loop"); + manual_update_done = true; + } + - if: + condition: + lambda: |- + static bool manual_update_triggered = false; + if (!manual_update_triggered && + id(update_test_component).get_loop_count() == 3 && + id(update_test_component).get_update_count() >= 3) { + manual_update_triggered = true; + return true; + } + return false; + then: + - component.update: update_test_component diff --git a/tests/integration/test_loop_disable_enable.py b/tests/integration/test_loop_disable_enable.py index d5f868aa93..e93fc32178 100644 --- a/tests/integration/test_loop_disable_enable.py +++ b/tests/integration/test_loop_disable_enable.py @@ -45,11 +45,18 @@ async def test_loop_disable_enable( isr_component_disabled = asyncio.Event() isr_component_re_enabled = asyncio.Event() isr_component_pure_re_enabled = asyncio.Event() + # Events for update component testing + update_component_loop_disabled = asyncio.Event() + update_component_manual_update_called = asyncio.Event() # Track loop counts for components self_disable_10_counts: list[int] = [] normal_component_counts: list[int] = [] isr_component_counts: list[int] = [] + # Track update component behavior + update_component_loop_count = 0 + update_component_update_count = 0 + update_component_manual_update_count = 0 def on_log_line(line: str) -> None: """Process each log line from the process output.""" @@ -59,6 +66,7 @@ async def test_loop_disable_enable( if ( "loop_test_component" not in clean_line and "loop_test_isr_component" not in clean_line + and "Manually calling component.update" not in clean_line ): return @@ -112,6 +120,23 @@ async def test_loop_disable_enable( elif "Running after pure ISR re-enable!" in clean_line: isr_component_pure_re_enabled.set() + # Update component events + elif "[update_test]" in clean_line: + if "LoopTestUpdateComponent loop count:" in clean_line: + nonlocal update_component_loop_count + update_component_loop_count = int( + clean_line.split("LoopTestUpdateComponent loop count: ")[1] + ) + elif "LoopTestUpdateComponent update() called" in clean_line: + nonlocal update_component_update_count + update_component_update_count += 1 + if "Manually calling component.update" in " ".join(log_messages[-5:]): + nonlocal update_component_manual_update_count + update_component_manual_update_count += 1 + update_component_manual_update_called.set() + elif "Disabling loop after" in clean_line: + update_component_loop_disabled.set() + # Write, compile and run the ESPHome device with log callback async with ( run_compiled(yaml_config, line_callback=on_log_line), @@ -205,3 +230,28 @@ async def test_loop_disable_enable( assert final_count > 10, ( f"Component didn't run after pure ISR enable: got {final_count} counts total" ) + + # Test component.update functionality when loop is disabled + # Wait for update component to disable its loop + try: + await asyncio.wait_for(update_component_loop_disabled.wait(), timeout=3.0) + except asyncio.TimeoutError: + pytest.fail("Update component did not disable its loop within 3 seconds") + + # Verify it ran exactly 3 loops before disabling + assert update_component_loop_count == 3, ( + f"Expected 3 loop iterations before disable, got {update_component_loop_count}" + ) + + # Wait for manual component.update to be called + try: + await asyncio.wait_for( + update_component_manual_update_called.wait(), timeout=5.0 + ) + except asyncio.TimeoutError: + pytest.fail("Manual component.update was not called within 5 seconds") + + # The key test: verify that manual component.update worked after loop was disabled + assert update_component_manual_update_count >= 1, ( + "component.update did not fire after loop was disabled" + ) From 37982290f7ac1203dc713dd028162226b061e295 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:35:55 +1200 Subject: [PATCH 79/90] Bump version to 2025.7.0b4 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index 42af703a94..e09116d202 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.7.0b3 +PROJECT_NUMBER = 2025.7.0b4 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index de15050d0c..44ec5ec9b9 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.7.0b3" +__version__ = "2025.7.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 6e90feeccf84e74e6f88224e944f20a647b6dabb Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Tue, 15 Jul 2025 21:33:15 +0200 Subject: [PATCH 80/90] [ms8607] Fix humidity calc (#9499) --- esphome/components/ms8607/ms8607.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ms8607/ms8607.cpp b/esphome/components/ms8607/ms8607.cpp index b985623b24..f8ea26bfd9 100644 --- a/esphome/components/ms8607/ms8607.cpp +++ b/esphome/components/ms8607/ms8607.cpp @@ -356,7 +356,7 @@ void MS8607Component::read_humidity_(float temperature_float) { // map 16 bit humidity value into range [-6%, 118%] float const humidity_partial = double(humidity) / (1 << 16); - float const humidity_percentage = lerp(humidity_partial, -6.0, 118.0); + float const humidity_percentage = std::lerp(-6.0, 118.0, humidity_partial); float const compensated_humidity_percentage = humidity_percentage + (20 - temperature_float) * MS8607_H_TEMP_COEFFICIENT; ESP_LOGD(TAG, "Compensated for temperature, humidity=%.2f%%", compensated_humidity_percentage); From bd0fe34b148ea93d01b3a0264b25e78c2cdf1267 Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Tue, 15 Jul 2025 21:33:15 +0200 Subject: [PATCH 81/90] [ms8607] Fix humidity calc (#9499) --- esphome/components/ms8607/ms8607.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ms8607/ms8607.cpp b/esphome/components/ms8607/ms8607.cpp index b985623b24..f8ea26bfd9 100644 --- a/esphome/components/ms8607/ms8607.cpp +++ b/esphome/components/ms8607/ms8607.cpp @@ -356,7 +356,7 @@ void MS8607Component::read_humidity_(float temperature_float) { // map 16 bit humidity value into range [-6%, 118%] float const humidity_partial = double(humidity) / (1 << 16); - float const humidity_percentage = lerp(humidity_partial, -6.0, 118.0); + float const humidity_percentage = std::lerp(-6.0, 118.0, humidity_partial); float const compensated_humidity_percentage = humidity_percentage + (20 - temperature_float) * MS8607_H_TEMP_COEFFICIENT; ESP_LOGD(TAG, "Compensated for temperature, humidity=%.2f%%", compensated_humidity_percentage); From 9769f8a4cc9d5167db84bf5ca18457d6ffea8d3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Jul 2025 11:51:01 -1000 Subject: [PATCH 82/90] Fix timing overflow when components disable themselves during loop (#9529) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/core/application.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index d6fab018cc..e19acd3ba6 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -309,6 +309,12 @@ void Application::disable_component_loop_(Component *component) { if (this->in_loop_ && i == this->current_loop_index_) { // Decrement so we'll process the swapped component next this->current_loop_index_--; + // Update the loop start time to current time so the swapped component + // gets correct timing instead of inheriting stale timing. + // This prevents integer underflow in timing calculations by ensuring + // the swapped component starts with a fresh timing reference, avoiding + // errors caused by stale or wrapped timing values. + this->loop_component_start_time_ = millis(); } } return; From 82120bc5d757b8220dd5283e5cc6db1c230823fd Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 15 Jul 2025 15:03:02 -0700 Subject: [PATCH 83/90] [as3935_spi] remove unnecessary includes (#9528) --- esphome/components/as3935_spi/as3935_spi.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/components/as3935_spi/as3935_spi.h b/esphome/components/as3935_spi/as3935_spi.h index 073f5c09a4..e5422f9b37 100644 --- a/esphome/components/as3935_spi/as3935_spi.h +++ b/esphome/components/as3935_spi/as3935_spi.h @@ -3,8 +3,6 @@ #include "esphome/core/component.h" #include "esphome/components/as3935/as3935.h" #include "esphome/components/spi/spi.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { namespace as3935_spi { From 8c8c08d40c1d04c47969e37747a3fd8dee190f39 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 15 Jul 2025 11:51:01 -1000 Subject: [PATCH 84/90] Fix timing overflow when components disable themselves during loop (#9529) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/core/application.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index d6fab018cc..e19acd3ba6 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -309,6 +309,12 @@ void Application::disable_component_loop_(Component *component) { if (this->in_loop_ && i == this->current_loop_index_) { // Decrement so we'll process the swapped component next this->current_loop_index_--; + // Update the loop start time to current time so the swapped component + // gets correct timing instead of inheriting stale timing. + // This prevents integer underflow in timing calculations by ensuring + // the swapped component starts with a fresh timing reference, avoiding + // errors caused by stale or wrapped timing values. + this->loop_component_start_time_ = millis(); } } return; From 4182076f642a433098668f0a0942b71aa2a1aea2 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 15 Jul 2025 15:03:02 -0700 Subject: [PATCH 85/90] [as3935_spi] remove unnecessary includes (#9528) --- esphome/components/as3935_spi/as3935_spi.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/esphome/components/as3935_spi/as3935_spi.h b/esphome/components/as3935_spi/as3935_spi.h index 073f5c09a4..e5422f9b37 100644 --- a/esphome/components/as3935_spi/as3935_spi.h +++ b/esphome/components/as3935_spi/as3935_spi.h @@ -3,8 +3,6 @@ #include "esphome/core/component.h" #include "esphome/components/as3935/as3935.h" #include "esphome/components/spi/spi.h" -#include "esphome/components/sensor/sensor.h" -#include "esphome/components/binary_sensor/binary_sensor.h" namespace esphome { namespace as3935_spi { From 90a16ffa891a37588d6cd1eea2bf9f1fa28c0149 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:45:20 +1200 Subject: [PATCH 86/90] Bump version to 2025.7.0b5 --- Doxyfile | 2 +- esphome/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index e09116d202..454db4b41b 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 2025.7.0b4 +PROJECT_NUMBER = 2025.7.0b5 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/esphome/const.py b/esphome/const.py index 44ec5ec9b9..cd86694a42 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -4,7 +4,7 @@ from enum import Enum from esphome.enum import StrEnum -__version__ = "2025.7.0b4" +__version__ = "2025.7.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 5d9cba3dce0ba882f3143fd70e69708bdb0d457a Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 16 Jul 2025 03:00:21 +0200 Subject: [PATCH 87/90] [nrf52, core] nrf52 core based on zephyr (#7049) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Samuel Sieb Co-authored-by: Tomasz Duda Co-authored-by: J. Nick Koston --- CODEOWNERS | 2 + esphome/components/logger/__init__.py | 23 ++ esphome/components/logger/logger.cpp | 13 +- esphome/components/logger/logger.h | 33 ++- esphome/components/logger/logger_zephyr.cpp | 88 +++++++ esphome/components/nrf52/__init__.py | 218 +++++++++++++++++ esphome/components/nrf52/boards.py | 34 +++ esphome/components/nrf52/const.py | 4 + esphome/components/nrf52/gpio.py | 53 ++++ esphome/components/time/__init__.py | 5 +- esphome/components/time/real_time_clock.cpp | 19 +- esphome/components/zephyr/__init__.py | 231 ++++++++++++++++++ esphome/components/zephyr/const.py | 14 ++ esphome/components/zephyr/core.cpp | 86 +++++++ esphome/components/zephyr/gpio.cpp | 120 +++++++++ esphome/components/zephyr/gpio.h | 38 +++ esphome/components/zephyr/pre_build.py.script | 4 + esphome/components/zephyr/preferences.cpp | 156 ++++++++++++ esphome/components/zephyr/preferences.h | 13 + esphome/const.py | 6 + esphome/core/__init__.py | 9 + esphome/core/helpers.h | 3 +- .../components/gpio/test.nrf52-adafruit.yaml | 14 ++ tests/components/gpio/test.nrf52-mcumgr.yaml | 14 ++ .../logger/test.nrf52-adafruit.yaml | 7 + .../components/logger/test.nrf52-mcumgr.yaml | 7 + .../components/time/test.nrf52-adafruit.yaml | 1 + tests/components/time/test.nrf52-mcumgr.yaml | 1 + .../uptime/test.nrf52-adafruit.yaml | 10 + .../components/uptime/test.nrf52-mcumgr.yaml | 10 + .../build_components_base.nrf52-adafruit.yaml | 16 ++ .../build_components_base.nrf52-mcumgr.yaml | 15 ++ 32 files changed, 1250 insertions(+), 17 deletions(-) create mode 100644 esphome/components/logger/logger_zephyr.cpp create mode 100644 esphome/components/nrf52/__init__.py create mode 100644 esphome/components/nrf52/boards.py create mode 100644 esphome/components/nrf52/const.py create mode 100644 esphome/components/nrf52/gpio.py create mode 100644 esphome/components/zephyr/__init__.py create mode 100644 esphome/components/zephyr/const.py create mode 100644 esphome/components/zephyr/core.cpp create mode 100644 esphome/components/zephyr/gpio.cpp create mode 100644 esphome/components/zephyr/gpio.h create mode 100644 esphome/components/zephyr/pre_build.py.script create mode 100644 esphome/components/zephyr/preferences.cpp create mode 100644 esphome/components/zephyr/preferences.h create mode 100644 tests/components/gpio/test.nrf52-adafruit.yaml create mode 100644 tests/components/gpio/test.nrf52-mcumgr.yaml create mode 100644 tests/components/logger/test.nrf52-adafruit.yaml create mode 100644 tests/components/logger/test.nrf52-mcumgr.yaml create mode 100644 tests/components/time/test.nrf52-adafruit.yaml create mode 100644 tests/components/time/test.nrf52-mcumgr.yaml create mode 100644 tests/components/uptime/test.nrf52-adafruit.yaml create mode 100644 tests/components/uptime/test.nrf52-mcumgr.yaml create mode 100644 tests/test_build_components/build_components_base.nrf52-adafruit.yaml create mode 100644 tests/test_build_components/build_components_base.nrf52-mcumgr.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 2975080ba9..b5037a6f9f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -324,6 +324,7 @@ esphome/components/nextion/text_sensor/* @senexcrenshaw esphome/components/nfc/* @jesserockz @kbx81 esphome/components/noblex/* @AGalfra esphome/components/npi19/* @bakerkj +esphome/components/nrf52/* @tomaszduda23 esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb esphome/components/online_image/* @clydebarrow @guillempages @@ -535,5 +536,6 @@ esphome/components/xiaomi_xmwsdj04mmc/* @medusalix esphome/components/xl9535/* @mreditor97 esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 esphome/components/xxtea/* @clydebarrow +esphome/components/zephyr/* @tomaszduda23 esphome/components/zhlt01/* @cfeenstra1024 esphome/components/zio_ultrasonic/* @kahrendt diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 9ac2999696..c055facd6c 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -21,6 +21,11 @@ from esphome.components.libretiny.const import ( COMPONENT_LN882X, COMPONENT_RTL87XX, ) +from esphome.components.zephyr import ( + zephyr_add_cdc_acm, + zephyr_add_overlay, + zephyr_add_prj_conf, +) from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( @@ -41,6 +46,7 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_LN882X, + PLATFORM_NRF52, PLATFORM_RP2040, PLATFORM_RTL87XX, PlatformFramework, @@ -115,6 +121,8 @@ ESP_ARDUINO_UNSUPPORTED_USB_UARTS = [USB_SERIAL_JTAG] UART_SELECTION_RP2040 = [USB_CDC, UART0, UART1] +UART_SELECTION_NRF52 = [USB_CDC, UART0] + HARDWARE_UART_TO_UART_SELECTION = { UART0: logger_ns.UART_SELECTION_UART0, UART0_SWAP: logger_ns.UART_SELECTION_UART0_SWAP, @@ -167,6 +175,8 @@ def uart_selection(value): return cv.one_of(*UART_SELECTION_LIBRETINY[component], upper=True)(value) if CORE.is_host: raise cv.Invalid("Uart selection not valid for host platform") + if CORE.is_nrf52: + return cv.one_of(*UART_SELECTION_NRF52, upper=True)(value) raise NotImplementedError @@ -186,6 +196,7 @@ LoggerMessageTrigger = logger_ns.class_( automation.Trigger.template(cg.int_, cg.const_char_ptr, cg.const_char_ptr), ) + CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = "esp8266_store_log_strings_in_flash" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -227,6 +238,7 @@ CONFIG_SCHEMA = cv.All( bk72xx=DEFAULT, ln882x=DEFAULT, rtl87xx=DEFAULT, + nrf52=USB_CDC, ): cv.All( cv.only_on( [ @@ -236,6 +248,7 @@ CONFIG_SCHEMA = cv.All( PLATFORM_BK72XX, PLATFORM_LN882X, PLATFORM_RTL87XX, + PLATFORM_NRF52, ] ), uart_selection, @@ -358,6 +371,15 @@ async def to_code(config): except cv.Invalid: pass + if CORE.using_zephyr: + if config[CONF_HARDWARE_UART] == UART0: + zephyr_add_overlay("""&uart0 { status = "okay";};""") + if config[CONF_HARDWARE_UART] == UART1: + zephyr_add_overlay("""&uart1 { status = "okay";};""") + if config[CONF_HARDWARE_UART] == USB_CDC: + zephyr_add_prj_conf("UART_LINE_CTRL", True) + zephyr_add_cdc_acm(config, 0) + # Register at end for safe mode await cg.register_component(log, config) @@ -462,6 +484,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( PlatformFramework.RTL87XX_ARDUINO, PlatformFramework.LN882X_ARDUINO, }, + "logger_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR}, "task_log_buffer.cpp": { PlatformFramework.ESP32_ARDUINO, PlatformFramework.ESP32_IDF, diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index db807f7e53..01a7565699 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -4,9 +4,9 @@ #include // For unique_ptr #endif +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace logger { @@ -160,6 +160,8 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT #if defined(USE_ESP32) || defined(USE_LIBRETINY) this->main_task_ = xTaskGetCurrentTaskHandle(); +#elif defined(USE_ZEPHYR) + this->main_task_ = k_current_get(); #endif } #ifdef USE_ESPHOME_TASK_LOG_BUFFER @@ -172,6 +174,7 @@ void Logger::init_log_buffer(size_t total_buffer_size) { } #endif +#ifndef USE_ZEPHYR #if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32) void Logger::loop() { #if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO) @@ -185,8 +188,13 @@ void Logger::loop() { } opened = !opened; } +#endif + this->process_messages_(); +} +#endif #endif +void Logger::process_messages_() { #ifdef USE_ESPHOME_TASK_LOG_BUFFER // Process any buffered messages when available if (this->log_buffer_->has_messages()) { @@ -227,12 +235,11 @@ void Logger::loop() { } #endif } -#endif void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; } void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; } -#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) UARTSelection Logger::get_uart() const { return this->uart_; } #endif diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index fb68e75a51..6bd5bb66ed 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -29,6 +29,11 @@ #include #endif // USE_ESP_IDF +#ifdef USE_ZEPHYR +#include +struct device; +#endif + namespace esphome { namespace logger { @@ -56,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = { "VV", // VERY_VERBOSE }; -#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) /** Enum for logging UART selection * * Advanced configuration (pin selection, etc) is not supported. @@ -82,7 +87,7 @@ enum UARTSelection : uint8_t { UART_SELECTION_UART0_SWAP, #endif // USE_ESP8266 }; -#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY +#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY || USE_ZEPHYR /** * @brief Logger component for all ESPHome logging. @@ -107,7 +112,7 @@ class Logger : public Component { #ifdef USE_ESPHOME_TASK_LOG_BUFFER void init_log_buffer(size_t total_buffer_size); #endif -#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32) +#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32) || defined(USE_ZEPHYR) void loop() override; #endif /// Manually set the baud rate for serial, set to 0 to disable. @@ -122,7 +127,7 @@ class Logger : public Component { #ifdef USE_ESP32 void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); } #endif -#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; } /// Get the UART used by the logger. UARTSelection get_uart() const; @@ -157,6 +162,7 @@ class Logger : public Component { #endif protected: + void process_messages_(); void write_msg_(const char *msg); // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator @@ -164,7 +170,7 @@ class Logger : public Component { inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format, va_list args, char *buffer, uint16_t *buffer_at, uint16_t buffer_size) { -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size); #else this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size); @@ -231,7 +237,10 @@ class Logger : public Component { #ifdef USE_ARDUINO Stream *hw_serial_{nullptr}; #endif -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +#if defined(USE_ZEPHYR) + const device *uart_dev_{nullptr}; +#endif +#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) void *main_task_ = nullptr; // Only used for thread name identification #endif #ifdef USE_ESP32 @@ -256,7 +265,7 @@ class Logger : public Component { uint16_t tx_buffer_at_{0}; uint16_t tx_buffer_size_{0}; uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE}; -#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) +#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR) UARTSelection uart_{UART_SELECTION_UART0}; #endif #ifdef USE_LIBRETINY @@ -268,9 +277,13 @@ class Logger : public Component { bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms #endif -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) const char *HOT get_thread_name_() { +#ifdef USE_ZEPHYR + k_tid_t current_task = k_current_get(); +#else TaskHandle_t current_task = xTaskGetCurrentTaskHandle(); +#endif if (current_task == main_task_) { return nullptr; // Main task } else { @@ -278,6 +291,8 @@ class Logger : public Component { return pcTaskGetName(current_task); #elif defined(USE_LIBRETINY) return pcTaskGetTaskName(current_task); +#elif defined(USE_ZEPHYR) + return k_thread_name_get(current_task); #endif } } @@ -319,7 +334,7 @@ class Logger : public Component { const char *color = esphome::logger::LOG_LEVEL_COLORS[level]; const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level]; -#if defined(USE_ESP32) || defined(USE_LIBRETINY) +#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) if (thread_name != nullptr) { // Non-main task with thread name this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line, diff --git a/esphome/components/logger/logger_zephyr.cpp b/esphome/components/logger/logger_zephyr.cpp new file mode 100644 index 0000000000..35ef2e9561 --- /dev/null +++ b/esphome/components/logger/logger_zephyr.cpp @@ -0,0 +1,88 @@ +#ifdef USE_ZEPHYR + +#include "esphome/core/application.h" +#include "esphome/core/log.h" +#include "logger.h" + +#include +#include +#include + +namespace esphome { +namespace logger { + +static const char *const TAG = "logger"; + +void Logger::loop() { +#ifdef USE_LOGGER_USB_CDC + if (this->uart_ != UART_SELECTION_USB_CDC || nullptr == this->uart_dev_) { + return; + } + static bool opened = false; + uint32_t dtr = 0; + uart_line_ctrl_get(this->uart_dev_, UART_LINE_CTRL_DTR, &dtr); + + /* Poll if the DTR flag was set, optional */ + if (opened == dtr) { + return; + } + + if (!opened) { + App.schedule_dump_config(); + } + opened = !opened; +#endif + this->process_messages_(); +} + +void Logger::pre_setup() { + if (this->baud_rate_ > 0) { + static const struct device *uart_dev = nullptr; + switch (this->uart_) { + case UART_SELECTION_UART0: + uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart0)); + break; + case UART_SELECTION_UART1: + uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart1)); + break; +#ifdef USE_LOGGER_USB_CDC + case UART_SELECTION_USB_CDC: + uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(cdc_acm_uart0)); + if (device_is_ready(uart_dev)) { + usb_enable(nullptr); + } + break; +#endif + } + if (!device_is_ready(uart_dev)) { + ESP_LOGE(TAG, "%s is not ready.", get_uart_selection_()); + } else { + this->uart_dev_ = uart_dev; + } + } + global_logger = this; + ESP_LOGI(TAG, "Log initialized"); +} + +void HOT Logger::write_msg_(const char *msg) { +#ifdef CONFIG_PRINTK + printk("%s\n", msg); +#endif + if (nullptr == this->uart_dev_) { + return; + } + while (*msg) { + uart_poll_out(this->uart_dev_, *msg); + ++msg; + } + uart_poll_out(this->uart_dev_, '\n'); +} + +const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"}; + +const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; } + +} // namespace logger +} // namespace esphome + +#endif diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py new file mode 100644 index 0000000000..c23298e38f --- /dev/null +++ b/esphome/components/nrf52/__init__.py @@ -0,0 +1,218 @@ +from __future__ import annotations + +from pathlib import Path + +import esphome.codegen as cg +from esphome.components.zephyr import ( + copy_files as zephyr_copy_files, + zephyr_add_pm_static, + zephyr_set_core_data, + zephyr_to_code, +) +from esphome.components.zephyr.const import ( + BOOTLOADER_MCUBOOT, + KEY_BOOTLOADER, + KEY_ZEPHYR, +) +import esphome.config_validation as cv +from esphome.const import ( + CONF_BOARD, + CONF_FRAMEWORK, + KEY_CORE, + KEY_FRAMEWORK_VERSION, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PLATFORM_NRF52, +) +from esphome.core import CORE, EsphomeError, coroutine_with_priority +from esphome.storage_json import StorageJSON +from esphome.types import ConfigType + +from .boards import BOARDS_ZEPHYR, BOOTLOADER_CONFIG +from .const import ( + BOOTLOADER_ADAFRUIT, + BOOTLOADER_ADAFRUIT_NRF52_SD132, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, +) + +# force import gpio to register pin schema +from .gpio import nrf52_pin_to_code # noqa + +CODEOWNERS = ["@tomaszduda23"] +AUTO_LOAD = ["zephyr"] +IS_TARGET_PLATFORM = True + + +def set_core_data(config: ConfigType) -> ConfigType: + zephyr_set_core_data(config) + CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_NRF52 + CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = KEY_ZEPHYR + CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(2, 6, 1) + + if config[KEY_BOOTLOADER] in BOOTLOADER_CONFIG: + zephyr_add_pm_static(BOOTLOADER_CONFIG[config[KEY_BOOTLOADER]]) + + return config + + +BOOTLOADERS = [ + BOOTLOADER_ADAFRUIT, + BOOTLOADER_ADAFRUIT_NRF52_SD132, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, + BOOTLOADER_MCUBOOT, +] + + +def _detect_bootloader(config: ConfigType) -> ConfigType: + """Detect the bootloader for the given board.""" + config = config.copy() + bootloaders: list[str] = [] + board = config[CONF_BOARD] + + if board in BOARDS_ZEPHYR and KEY_BOOTLOADER in BOARDS_ZEPHYR[board]: + # this board have bootloaders config available + bootloaders = BOARDS_ZEPHYR[board][KEY_BOOTLOADER] + + if KEY_BOOTLOADER not in config: + if bootloaders: + # there is no bootloader in config -> take first one + config[KEY_BOOTLOADER] = bootloaders[0] + else: + # make mcuboot as default if there is no configuration for that board + config[KEY_BOOTLOADER] = BOOTLOADER_MCUBOOT + elif bootloaders and config[KEY_BOOTLOADER] not in bootloaders: + raise cv.Invalid( + f"{board} does not support {config[KEY_BOOTLOADER]}, select one of: {', '.join(bootloaders)}" + ) + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_BOARD): cv.string_strict, + cv.Optional(KEY_BOOTLOADER): cv.one_of(*BOOTLOADERS, lower=True), + } + ), + _detect_bootloader, + set_core_data, +) + + +@coroutine_with_priority(1000) +async def to_code(config: ConfigType) -> None: + """Convert the configuration to code.""" + cg.add_platformio_option("board", config[CONF_BOARD]) + cg.add_build_flag("-DUSE_NRF52") + cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) + cg.add_define("ESPHOME_VARIANT", "NRF52") + cg.add_platformio_option(CONF_FRAMEWORK, CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK]) + cg.add_platformio_option( + "platform", + "https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-1.zip", + ) + cg.add_platformio_option( + "platform_packages", + [ + "platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-4.zip", + "platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.16.1-1.zip", + ], + ) + + if config[KEY_BOOTLOADER] == BOOTLOADER_ADAFRUIT: + # make sure that firmware.zip is created + # for Adafruit_nRF52_Bootloader + cg.add_platformio_option("board_upload.protocol", "nrfutil") + cg.add_platformio_option("board_upload.use_1200bps_touch", "true") + cg.add_platformio_option("board_upload.require_upload_port", "true") + cg.add_platformio_option("board_upload.wait_for_upload_port", "true") + + zephyr_to_code(config) + + +def copy_files() -> None: + """Copy files to the build directory.""" + zephyr_copy_files() + + +def get_download_types(storage_json: StorageJSON) -> list[dict[str, str]]: + """Get the download types for the firmware.""" + types = [] + UF2_PATH = "zephyr/zephyr.uf2" + DFU_PATH = "firmware.zip" + HEX_PATH = "zephyr/zephyr.hex" + HEX_MERGED_PATH = "zephyr/merged.hex" + APP_IMAGE_PATH = "zephyr/app_update.bin" + build_dir = Path(storage_json.firmware_bin_path).parent + if (build_dir / UF2_PATH).is_file(): + types = [ + { + "title": "UF2 package (recommended)", + "description": "For flashing via Adafruit nRF52 Bootloader as a flash drive.", + "file": UF2_PATH, + "download": f"{storage_json.name}.uf2", + }, + { + "title": "DFU package", + "description": "For flashing via adafruit-nrfutil using USB CDC.", + "file": DFU_PATH, + "download": f"dfu-{storage_json.name}.zip", + }, + ] + else: + types = [ + { + "title": "HEX package", + "description": "For flashing via pyocd using SWD.", + "file": ( + HEX_MERGED_PATH + if (build_dir / HEX_MERGED_PATH).is_file() + else HEX_PATH + ), + "download": f"{storage_json.name}.hex", + }, + ] + if (build_dir / APP_IMAGE_PATH).is_file(): + types += [ + { + "title": "App update package", + "description": "For flashing via mcumgr-web using BLE or smpclient using USB CDC.", + "file": APP_IMAGE_PATH, + "download": f"app-{storage_json.name}.img", + }, + ] + + return types + + +def _upload_using_platformio( + config: ConfigType, port: str, upload_args: list[str] +) -> int | str: + from esphome import platformio_api + + if port is not None: + upload_args += ["--upload-port", port] + return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args) + + +def upload_program(config: ConfigType, args, host: str) -> bool: + from esphome.__main__ import check_permissions, get_port_type + + result = 0 + handled = False + + if get_port_type(host) == "SERIAL": + check_permissions(host) + result = _upload_using_platformio(config, host, ["-t", "upload"]) + handled = True + + if host == "PYOCD": + result = _upload_using_platformio(config, host, ["-t", "flash_pyocd"]) + handled = True + + if result != 0: + raise EsphomeError(f"Upload failed with result: {result}") + + return handled diff --git a/esphome/components/nrf52/boards.py b/esphome/components/nrf52/boards.py new file mode 100644 index 0000000000..8e5fb2a23d --- /dev/null +++ b/esphome/components/nrf52/boards.py @@ -0,0 +1,34 @@ +from esphome.components.zephyr import Section +from esphome.components.zephyr.const import KEY_BOOTLOADER + +from .const import ( + BOOTLOADER_ADAFRUIT, + BOOTLOADER_ADAFRUIT_NRF52_SD132, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, +) + +BOARDS_ZEPHYR = { + "adafruit_itsybitsy_nrf52840": { + KEY_BOOTLOADER: [ + BOOTLOADER_ADAFRUIT, + BOOTLOADER_ADAFRUIT_NRF52_SD132, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, + ] + }, +} + +# https://github.com/ffenix113/zigbee_home/blob/17bb7b9e9d375e756da9e38913f53303937fb66a/types/board/known_boards.go +# https://learn.adafruit.com/introducing-the-adafruit-nrf52840-feather?view=all#hathach-memory-map +BOOTLOADER_CONFIG = { + BOOTLOADER_ADAFRUIT_NRF52_SD132: [ + Section("empty_app_offset", 0x0, 0x26000, "flash_primary"), + ], + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6: [ + Section("empty_app_offset", 0x0, 0x26000, "flash_primary"), + ], + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7: [ + Section("empty_app_offset", 0x0, 0x27000, "flash_primary"), + ], +} diff --git a/esphome/components/nrf52/const.py b/esphome/components/nrf52/const.py new file mode 100644 index 0000000000..d827e5fb22 --- /dev/null +++ b/esphome/components/nrf52/const.py @@ -0,0 +1,4 @@ +BOOTLOADER_ADAFRUIT = "adafruit" +BOOTLOADER_ADAFRUIT_NRF52_SD132 = "adafruit_nrf52_sd132" +BOOTLOADER_ADAFRUIT_NRF52_SD140_V6 = "adafruit_nrf52_sd140_v6" +BOOTLOADER_ADAFRUIT_NRF52_SD140_V7 = "adafruit_nrf52_sd140_v7" diff --git a/esphome/components/nrf52/gpio.py b/esphome/components/nrf52/gpio.py new file mode 100644 index 0000000000..85230c1f57 --- /dev/null +++ b/esphome/components/nrf52/gpio.py @@ -0,0 +1,53 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components.zephyr.const import zephyr_ns +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INVERTED, CONF_MODE, CONF_NUMBER, PLATFORM_NRF52 + +ZephyrGPIOPin = zephyr_ns.class_("ZephyrGPIOPin", cg.InternalGPIOPin) + + +def _translate_pin(value): + if isinstance(value, dict) or value is None: + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) + if isinstance(value, int): + return value + try: + return int(value) + except ValueError: + pass + # e.g. P0.27 + if len(value) >= len("P0.0") and value[0] == "P" and value[2] == ".": + return cv.int_(value[len("P")].strip()) * 32 + cv.int_( + value[len("P0.") :].strip() + ) + raise cv.Invalid(f"Invalid pin: {value}") + + +def validate_gpio_pin(value): + value = _translate_pin(value) + if value < 0 or value > (32 + 16): + raise cv.Invalid(f"NRF52: Invalid pin number: {value}") + return value + + +NRF52_PIN_SCHEMA = cv.All( + pins.gpio_base_schema( + ZephyrGPIOPin, + validate_gpio_pin, + modes=pins.GPIO_STANDARD_MODES, + ), +) + + +@pins.PIN_SCHEMA_REGISTRY.register(PLATFORM_NRF52, NRF52_PIN_SCHEMA) +async def nrf52_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index ab821d457b..58d35c4baf 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -6,6 +6,7 @@ import tzlocal from esphome import automation from esphome.automation import Condition import esphome.codegen as cg +from esphome.components.zephyr import zephyr_add_prj_conf import esphome.config_validation as cv from esphome.const import ( CONF_AT, @@ -25,7 +26,7 @@ from esphome.const import ( CONF_TIMEZONE, CONF_TRIGGER_ID, ) -from esphome.core import coroutine_with_priority +from esphome.core import CORE, coroutine_with_priority _LOGGER = logging.getLogger(__name__) @@ -341,6 +342,8 @@ async def register_time(time_var, config): @coroutine_with_priority(100.0) async def to_code(config): + if CORE.using_zephyr: + zephyr_add_prj_conf("POSIX_CLOCK", True) cg.add_define("USE_TIME") cg.add_global(time_ns.using) diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 61391d2c6b..42c564659f 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -2,13 +2,15 @@ #include "esphome/core/log.h" #ifdef USE_HOST #include +#elif defined(USE_ZEPHYR) +#include #else #include "lwip/opt.h" #endif #ifdef USE_ESP8266 #include "sys/time.h" #endif -#ifdef USE_RP2040 +#if defined(USE_RP2040) || defined(USE_ZEPHYR) #include #endif #include @@ -22,11 +24,22 @@ static const char *const TAG = "time"; RealTimeClock::RealTimeClock() = default; void RealTimeClock::synchronize_epoch_(uint32_t epoch) { + ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch); // Update UTC epoch time. +#ifdef USE_ZEPHYR + struct timespec ts; + ts.tv_nsec = 0; + ts.tv_sec = static_cast(epoch); + + int ret = clock_settime(CLOCK_REALTIME, &ts); + + if (ret != 0) { + ESP_LOGW(TAG, "clock_settime() failed with code %d", ret); + } +#else struct timeval timev { .tv_sec = static_cast(epoch), .tv_usec = 0, }; - ESP_LOGVV(TAG, "Got epoch %" PRIu32, epoch); struct timezone tz = {0, 0}; int ret = settimeofday(&timev, &tz); if (ret == EINVAL) { @@ -43,7 +56,7 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { if (ret != 0) { ESP_LOGW(TAG, "setimeofday() failed with code %d", ret); } - +#endif auto time = this->now(); ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour, time.minute, time.second); diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py new file mode 100644 index 0000000000..2b542404a5 --- /dev/null +++ b/esphome/components/zephyr/__init__.py @@ -0,0 +1,231 @@ +import os +from typing import Final, TypedDict + +import esphome.codegen as cg +from esphome.const import CONF_BOARD +from esphome.core import CORE +from esphome.helpers import copy_file_if_changed, write_file_if_changed + +from .const import ( + BOOTLOADER_MCUBOOT, + KEY_BOOTLOADER, + KEY_EXTRA_BUILD_FILES, + KEY_OVERLAY, + KEY_PM_STATIC, + KEY_PRJ_CONF, + KEY_ZEPHYR, + zephyr_ns, +) + +CODEOWNERS = ["@tomaszduda23"] +AUTO_LOAD = ["preferences"] +KEY_BOARD: Final = "board" + +PrjConfValueType = bool | str | int + + +class Section: + def __init__(self, name, address, size, region): + self.name = name + self.address = address + self.size = size + self.region = region + self.end_address = self.address + self.size + + def __str__(self): + return ( + f"{self.name}:\n" + f" address: 0x{self.address:X}\n" + f" end_address: 0x{self.end_address:X}\n" + f" region: {self.region}\n" + f" size: 0x{self.size:X}" + ) + + +class ZephyrData(TypedDict): + board: str + bootloader: str + prj_conf: dict[str, tuple[PrjConfValueType, bool]] + overlay: str + extra_build_files: dict[str, str] + pm_static: list[Section] + + +def zephyr_set_core_data(config): + CORE.data[KEY_ZEPHYR] = ZephyrData( + board=config[CONF_BOARD], + bootloader=config[KEY_BOOTLOADER], + prj_conf={}, + overlay="", + extra_build_files={}, + pm_static=[], + ) + return config + + +def zephyr_data() -> ZephyrData: + return CORE.data[KEY_ZEPHYR] + + +def zephyr_add_prj_conf( + name: str, value: PrjConfValueType, required: bool = True +) -> None: + """Set an zephyr prj conf value.""" + if not name.startswith("CONFIG_"): + name = "CONFIG_" + name + prj_conf = zephyr_data()[KEY_PRJ_CONF] + if name not in prj_conf: + prj_conf[name] = (value, required) + return + old_value, old_required = prj_conf[name] + if old_value != value and old_required: + raise ValueError( + f"{name} already set with value '{old_value}', cannot set again to '{value}'" + ) + if required: + prj_conf[name] = (value, required) + + +def zephyr_add_overlay(content): + zephyr_data()[KEY_OVERLAY] += content + + +def add_extra_build_file(filename: str, path: str) -> bool: + """Add an extra build file to the project.""" + extra_build_files = zephyr_data()[KEY_EXTRA_BUILD_FILES] + if filename not in extra_build_files: + extra_build_files[filename] = path + return True + return False + + +def add_extra_script(stage: str, filename: str, path: str): + """Add an extra script to the project.""" + key = f"{stage}:{filename}" + if add_extra_build_file(filename, path): + cg.add_platformio_option("extra_scripts", [key]) + + +def zephyr_to_code(config): + cg.add(zephyr_ns.setup_preferences()) + cg.add_build_flag("-DUSE_ZEPHYR") + cg.set_cpp_standard("gnu++20") + # build is done by west so bypass board checking in platformio + cg.add_platformio_option("boards_dir", CORE.relative_build_path("boards")) + + # c++ support + zephyr_add_prj_conf("NEWLIB_LIBC", True) + zephyr_add_prj_conf("CONFIG_FPU", True) + zephyr_add_prj_conf("NEWLIB_LIBC_FLOAT_PRINTF", True) + zephyr_add_prj_conf("CPLUSPLUS", True) + zephyr_add_prj_conf("CONFIG_STD_CPP20", True) + zephyr_add_prj_conf("LIB_CPLUSPLUS", True) + # preferences + zephyr_add_prj_conf("SETTINGS", True) + zephyr_add_prj_conf("NVS", True) + zephyr_add_prj_conf("FLASH_MAP", True) + zephyr_add_prj_conf("CONFIG_FLASH", True) + # watchdog + zephyr_add_prj_conf("WATCHDOG", True) + zephyr_add_prj_conf("WDT_DISABLE_AT_BOOT", False) + # disable console + zephyr_add_prj_conf("UART_CONSOLE", False) + zephyr_add_prj_conf("CONSOLE", False, False) + # use NFC pins as GPIO + zephyr_add_prj_conf("NFCT_PINS_AS_GPIOS", True) + + # os: ***** USAGE FAULT ***** + # os: Illegal load of EXC_RETURN into PC + zephyr_add_prj_conf("MAIN_STACK_SIZE", 2048) + + add_extra_script( + "pre", + "pre_build.py", + os.path.join(os.path.dirname(__file__), "pre_build.py.script"), + ) + + +def _format_prj_conf_val(value: PrjConfValueType) -> str: + if isinstance(value, bool): + return "y" if value else "n" + if isinstance(value, int): + return str(value) + if isinstance(value, str): + return f'"{value}"' + raise ValueError + + +def zephyr_add_cdc_acm(config, id): + zephyr_add_prj_conf("USB_DEVICE_STACK", True) + zephyr_add_prj_conf("USB_CDC_ACM", True) + # prevent device to go to susspend, without this communication stop working in python + # there should be a way to solve it + zephyr_add_prj_conf("USB_DEVICE_REMOTE_WAKEUP", False) + # prevent logging when buffer is full + zephyr_add_prj_conf("USB_CDC_ACM_LOG_LEVEL_WRN", True) + zephyr_add_overlay( + f""" +&zephyr_udc0 {{ + cdc_acm_uart{id}: cdc_acm_uart{id} {{ + compatible = "zephyr,cdc-acm-uart"; + }}; +}}; +""" + ) + + +def zephyr_add_pm_static(section: Section): + CORE.data[KEY_ZEPHYR][KEY_PM_STATIC].extend(section) + + +def copy_files(): + want_opts = zephyr_data()[KEY_PRJ_CONF] + + prj_conf = ( + "\n".join( + f"{name}={_format_prj_conf_val(value[0])}" + for name, value in sorted(want_opts.items()) + ) + + "\n" + ) + + write_file_if_changed(CORE.relative_build_path("zephyr/prj.conf"), prj_conf) + + write_file_if_changed( + CORE.relative_build_path("zephyr/app.overlay"), + zephyr_data()[KEY_OVERLAY], + ) + + if zephyr_data()[KEY_BOOTLOADER] == BOOTLOADER_MCUBOOT or zephyr_data()[ + KEY_BOARD + ] in ["xiao_ble"]: + fake_board_manifest = """ +{ +"frameworks": [ + "zephyr" +], +"name": "esphome nrf52", +"upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104 +}, +"url": "https://esphome.io/", +"vendor": "esphome" +} +""" + write_file_if_changed( + CORE.relative_build_path(f"boards/{zephyr_data()[KEY_BOARD]}.json"), + fake_board_manifest, + ) + + for filename, path in zephyr_data()[KEY_EXTRA_BUILD_FILES].items(): + copy_file_if_changed( + path, + CORE.relative_build_path(filename), + ) + + pm_static = "\n".join(str(item) for item in zephyr_data()[KEY_PM_STATIC]) + if pm_static: + write_file_if_changed( + CORE.relative_build_path("zephyr/pm_static.yml"), pm_static + ) diff --git a/esphome/components/zephyr/const.py b/esphome/components/zephyr/const.py new file mode 100644 index 0000000000..f14a326344 --- /dev/null +++ b/esphome/components/zephyr/const.py @@ -0,0 +1,14 @@ +from typing import Final + +import esphome.codegen as cg + +BOOTLOADER_MCUBOOT = "mcuboot" + +KEY_BOOTLOADER: Final = "bootloader" +KEY_EXTRA_BUILD_FILES: Final = "extra_build_files" +KEY_OVERLAY: Final = "overlay" +KEY_PM_STATIC: Final = "pm_static" +KEY_PRJ_CONF: Final = "prj_conf" +KEY_ZEPHYR = "zephyr" + +zephyr_ns = cg.esphome_ns.namespace("zephyr") diff --git a/esphome/components/zephyr/core.cpp b/esphome/components/zephyr/core.cpp new file mode 100644 index 0000000000..39b01f8abe --- /dev/null +++ b/esphome/components/zephyr/core.cpp @@ -0,0 +1,86 @@ +#ifdef USE_ZEPHYR + +#include +#include +#include +#include +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" + +namespace esphome { + +static int wdt_channel_id = -1; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static const device *const WDT = DEVICE_DT_GET(DT_ALIAS(watchdog0)); + +void yield() { ::k_yield(); } +uint32_t millis() { return k_ticks_to_ms_floor32(k_uptime_ticks()); } +uint32_t micros() { return k_ticks_to_us_floor32(k_uptime_ticks()); } +void delayMicroseconds(uint32_t us) { ::k_usleep(us); } +void delay(uint32_t ms) { ::k_msleep(ms); } + +void arch_init() { + if (device_is_ready(WDT)) { + static wdt_timeout_cfg wdt_config{}; + wdt_config.flags = WDT_FLAG_RESET_SOC; + wdt_config.window.max = 2000; + wdt_channel_id = wdt_install_timeout(WDT, &wdt_config); + if (wdt_channel_id >= 0) { + wdt_setup(WDT, WDT_OPT_PAUSE_HALTED_BY_DBG | WDT_OPT_PAUSE_IN_SLEEP); + } + } +} + +void arch_feed_wdt() { + if (wdt_channel_id >= 0) { + wdt_feed(WDT, wdt_channel_id); + } +} + +void arch_restart() { sys_reboot(SYS_REBOOT_COLD); } +uint32_t arch_get_cpu_cycle_count() { return k_cycle_get_32(); } +uint32_t arch_get_cpu_freq_hz() { return sys_clock_hw_cycles_per_sec(); } +uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } + +Mutex::Mutex() { + auto *mutex = new k_mutex(); + this->handle_ = mutex; + k_mutex_init(mutex); +} +Mutex::~Mutex() { delete static_cast(this->handle_); } +void Mutex::lock() { k_mutex_lock(static_cast(this->handle_), K_FOREVER); } +bool Mutex::try_lock() { return k_mutex_lock(static_cast(this->handle_), K_NO_WAIT) == 0; } +void Mutex::unlock() { k_mutex_unlock(static_cast(this->handle_)); } + +IRAM_ATTR InterruptLock::InterruptLock() { state_ = irq_lock(); } +IRAM_ATTR InterruptLock::~InterruptLock() { irq_unlock(state_); } + +uint32_t random_uint32() { return rand(); } // NOLINT(cert-msc30-c, cert-msc50-cpp) +bool random_bytes(uint8_t *data, size_t len) { + sys_rand_get(data, len); + return true; +} + +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) + mac[0] = ((NRF_FICR->DEVICEADDR[1] & 0xFFFF) >> 8) | 0xC0; + mac[1] = NRF_FICR->DEVICEADDR[1] & 0xFFFF; + mac[2] = NRF_FICR->DEVICEADDR[0] >> 24; + mac[3] = NRF_FICR->DEVICEADDR[0] >> 16; + mac[4] = NRF_FICR->DEVICEADDR[0] >> 8; + mac[5] = NRF_FICR->DEVICEADDR[0]; +} + +} // namespace esphome + +void setup(); +void loop(); + +int main() { + setup(); + while (true) { + loop(); + esphome::yield(); + } + return 0; +} + +#endif diff --git a/esphome/components/zephyr/gpio.cpp b/esphome/components/zephyr/gpio.cpp new file mode 100644 index 0000000000..4b84910368 --- /dev/null +++ b/esphome/components/zephyr/gpio.cpp @@ -0,0 +1,120 @@ +#ifdef USE_ZEPHYR +#include "gpio.h" +#include +#include "esphome/core/log.h" + +namespace esphome { +namespace zephyr { + +static const char *const TAG = "zephyr"; + +static int flags_to_mode(gpio::Flags flags, bool inverted, bool value) { + int ret = 0; + if (flags & gpio::FLAG_INPUT) { + ret |= GPIO_INPUT; + } + if (flags & gpio::FLAG_OUTPUT) { + ret |= GPIO_OUTPUT; + if (value != inverted) { + ret |= GPIO_OUTPUT_INIT_HIGH; + } else { + ret |= GPIO_OUTPUT_INIT_LOW; + } + } + if (flags & gpio::FLAG_PULLUP) { + ret |= GPIO_PULL_UP; + } + if (flags & gpio::FLAG_PULLDOWN) { + ret |= GPIO_PULL_DOWN; + } + if (flags & gpio::FLAG_OPEN_DRAIN) { + ret |= GPIO_OPEN_DRAIN; + } + return ret; +} + +struct ISRPinArg { + uint8_t pin; + bool inverted; +}; + +ISRInternalGPIOPin ZephyrGPIOPin::to_isr() const { + auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory) + arg->pin = this->pin_; + arg->inverted = this->inverted_; + return ISRInternalGPIOPin((void *) arg); +} + +void ZephyrGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const { + // TODO +} + +void ZephyrGPIOPin::setup() { + const struct device *gpio = nullptr; + if (this->pin_ < 32) { +#define GPIO0 DT_NODELABEL(gpio0) +#if DT_NODE_HAS_STATUS(GPIO0, okay) + gpio = DEVICE_DT_GET(GPIO0); +#else +#error "gpio0 is disabled" +#endif + } else { +#define GPIO1 DT_NODELABEL(gpio1) +#if DT_NODE_HAS_STATUS(GPIO1, okay) + gpio = DEVICE_DT_GET(GPIO1); +#else +#error "gpio1 is disabled" +#endif + } + if (device_is_ready(gpio)) { + this->gpio_ = gpio; + } else { + ESP_LOGE(TAG, "gpio %u is not ready.", this->pin_); + return; + } + this->pin_mode(this->flags_); +} + +void ZephyrGPIOPin::pin_mode(gpio::Flags flags) { + if (nullptr == this->gpio_) { + return; + } + gpio_pin_configure(this->gpio_, this->pin_ % 32, flags_to_mode(flags, this->inverted_, this->value_)); +} + +std::string ZephyrGPIOPin::dump_summary() const { + char buffer[32]; + snprintf(buffer, sizeof(buffer), "GPIO%u, P%u.%u", this->pin_, this->pin_ / 32, this->pin_ % 32); + return buffer; +} + +bool ZephyrGPIOPin::digital_read() { + if (nullptr == this->gpio_) { + return false; + } + return bool(gpio_pin_get(this->gpio_, this->pin_ % 32) != this->inverted_); +} + +void ZephyrGPIOPin::digital_write(bool value) { + // make sure that value is not ignored since it can be inverted e.g. on switch side + // that way init state should be correct + this->value_ = value; + if (nullptr == this->gpio_) { + return; + } + gpio_pin_set(this->gpio_, this->pin_ % 32, value != this->inverted_ ? 1 : 0); +} +void ZephyrGPIOPin::detach_interrupt() const { + // TODO +} + +} // namespace zephyr + +bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { + // TODO + return false; +} + +} // namespace esphome + +#endif diff --git a/esphome/components/zephyr/gpio.h b/esphome/components/zephyr/gpio.h new file mode 100644 index 0000000000..f512ae4648 --- /dev/null +++ b/esphome/components/zephyr/gpio.h @@ -0,0 +1,38 @@ +#pragma once + +#ifdef USE_ZEPHYR +#include "esphome/core/hal.h" +struct device; +namespace esphome { +namespace zephyr { + +class ZephyrGPIOPin : public InternalGPIOPin { + public: + void set_pin(uint8_t pin) { this->pin_ = pin; } + void set_inverted(bool inverted) { this->inverted_ = inverted; } + void set_flags(gpio::Flags flags) { this->flags_ = flags; } + + void setup() override; + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + void detach_interrupt() const override; + ISRInternalGPIOPin to_isr() const override; + uint8_t get_pin() const override { return this->pin_; } + bool is_inverted() const override { return this->inverted_; } + gpio::Flags get_flags() const override { return flags_; } + + protected: + void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; + const device *gpio_ = nullptr; + bool value_ = false; +}; + +} // namespace zephyr +} // namespace esphome + +#endif // USE_ZEPHYR diff --git a/esphome/components/zephyr/pre_build.py.script b/esphome/components/zephyr/pre_build.py.script new file mode 100644 index 0000000000..3731fccf53 --- /dev/null +++ b/esphome/components/zephyr/pre_build.py.script @@ -0,0 +1,4 @@ +Import("env") + +board_config = env.BoardConfig() +board_config.update("frameworks", ["arduino", "zephyr"]) diff --git a/esphome/components/zephyr/preferences.cpp b/esphome/components/zephyr/preferences.cpp new file mode 100644 index 0000000000..d702366044 --- /dev/null +++ b/esphome/components/zephyr/preferences.cpp @@ -0,0 +1,156 @@ +#ifdef USE_ZEPHYR + +#include +#include "esphome/core/preferences.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace zephyr { + +static const char *const TAG = "zephyr.preferences"; + +#define ESPHOME_SETTINGS_KEY "esphome" + +class ZephyrPreferenceBackend : public ESPPreferenceBackend { + public: + ZephyrPreferenceBackend(uint32_t type) { this->type_ = type; } + ZephyrPreferenceBackend(uint32_t type, std::vector &&data) : data(std::move(data)) { this->type_ = type; } + + bool save(const uint8_t *data, size_t len) override { + this->data.resize(len); + std::memcpy(this->data.data(), data, len); + ESP_LOGVV(TAG, "save key: %u, len: %d", this->type_, len); + return true; + } + + bool load(uint8_t *data, size_t len) override { + if (len != this->data.size()) { + ESP_LOGE(TAG, "size of setting key %s changed, from: %u, to: %u", get_key().c_str(), this->data.size(), len); + return false; + } + std::memcpy(data, this->data.data(), len); + ESP_LOGVV(TAG, "load key: %u, len: %d", this->type_, len); + return true; + } + + uint32_t get_type() const { return this->type_; } + std::string get_key() const { return str_sprintf(ESPHOME_SETTINGS_KEY "/%" PRIx32, this->type_); } + + std::vector data; + + protected: + uint32_t type_ = 0; +}; + +class ZephyrPreferences : public ESPPreferences { + public: + void open() { + int err = settings_subsys_init(); + if (err) { + ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err); + return; + } + + static struct settings_handler settings_cb = { + .name = ESPHOME_SETTINGS_KEY, + .h_set = load_setting, + .h_export = export_settings, + }; + + err = settings_register(&settings_cb); + if (err) { + ESP_LOGE(TAG, "setting_register failed, err, %d", err); + return; + } + + err = settings_load_subtree(ESPHOME_SETTINGS_KEY); + if (err) { + ESP_LOGE(TAG, "Cannot load settings, err: %d", err); + return; + } + ESP_LOGD(TAG, "Loaded %u settings.", this->backends_.size()); + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { + return make_preference(length, type); + } + + ESPPreferenceObject make_preference(size_t length, uint32_t type) override { + for (auto *backend : this->backends_) { + if (backend->get_type() == type) { + return ESPPreferenceObject(backend); + } + } + printf("type %u size %u\n", type, this->backends_.size()); + auto *pref = new ZephyrPreferenceBackend(type); // NOLINT(cppcoreguidelines-owning-memory) + ESP_LOGD(TAG, "Add new setting %s.", pref->get_key().c_str()); + this->backends_.push_back(pref); + return ESPPreferenceObject(pref); + } + + bool sync() override { + ESP_LOGD(TAG, "Save settings"); + int err = settings_save(); + if (err) { + ESP_LOGE(TAG, "Cannot save settings, err: %d", err); + return false; + } + return true; + } + + bool reset() override { + ESP_LOGD(TAG, "Reset settings"); + for (auto *backend : this->backends_) { + // save empty delete data + backend->data.clear(); + } + sync(); + return true; + } + + protected: + std::vector backends_; + + static int load_setting(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { + auto type = parse_hex(name); + if (!type.has_value()) { + std::string full_name(ESPHOME_SETTINGS_KEY); + full_name += "/"; + full_name += name; + // Delete unusable keys. Otherwise it will stay in flash forever. + settings_delete(full_name.c_str()); + return 1; + } + std::vector data(len); + int err = read_cb(cb_arg, data.data(), len); + + ESP_LOGD(TAG, "load setting, name: %s(%u), len %u, err %u", name, *type, len, err); + auto *pref = new ZephyrPreferenceBackend(*type, std::move(data)); // NOLINT(cppcoreguidelines-owning-memory) + static_cast(global_preferences)->backends_.push_back(pref); + return 0; + } + + static int export_settings(int (*cb)(const char *name, const void *value, size_t val_len)) { + for (auto *backend : static_cast(global_preferences)->backends_) { + auto name = backend->get_key(); + int err = cb(name.c_str(), backend->data.data(), backend->data.size()); + ESP_LOGD(TAG, "save in flash, name %s, len %u, err %d", name.c_str(), backend->data.size(), err); + } + return 0; + } +}; + +void setup_preferences() { + auto *prefs = new ZephyrPreferences(); // NOLINT(cppcoreguidelines-owning-memory) + global_preferences = prefs; + prefs->open(); +} + +} // namespace zephyr + +ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace esphome + +#endif diff --git a/esphome/components/zephyr/preferences.h b/esphome/components/zephyr/preferences.h new file mode 100644 index 0000000000..6a37e41b46 --- /dev/null +++ b/esphome/components/zephyr/preferences.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef USE_ZEPHYR + +namespace esphome { +namespace zephyr { + +void setup_preferences(); + +} // namespace zephyr +} // namespace esphome + +#endif diff --git a/esphome/const.py b/esphome/const.py index a30df6ef35..333e822cfa 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -21,6 +21,7 @@ class Platform(StrEnum): HOST = "host" LIBRETINY_OLDSTYLE = "libretiny" LN882X = "ln882x" + NRF52 = "nrf52" RP2040 = "rp2040" RTL87XX = "rtl87xx" @@ -31,6 +32,7 @@ class Framework(StrEnum): ARDUINO = "arduino" ESP_IDF = "esp-idf" NATIVE = "host" + ZEPHYR = "zephyr" class PlatformFramework(Enum): @@ -47,6 +49,9 @@ class PlatformFramework(Enum): RTL87XX_ARDUINO = (Platform.RTL87XX, Framework.ARDUINO) LN882X_ARDUINO = (Platform.LN882X, Framework.ARDUINO) + # Zephyr framework platforms + NRF52_ZEPHYR = (Platform.NRF52, Framework.ZEPHYR) + # Host platform (native) HOST_NATIVE = (Platform.HOST, Framework.NATIVE) @@ -58,6 +63,7 @@ PLATFORM_ESP8266 = Platform.ESP8266 PLATFORM_HOST = Platform.HOST PLATFORM_LIBRETINY_OLDSTYLE = Platform.LIBRETINY_OLDSTYLE PLATFORM_LN882X = Platform.LN882X +PLATFORM_NRF52 = Platform.NRF52 PLATFORM_RP2040 = Platform.RP2040 PLATFORM_RTL87XX = Platform.RTL87XX diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index e33bbcf726..5ce2ed5caf 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -21,6 +21,7 @@ from esphome.const import ( PLATFORM_ESP8266, PLATFORM_HOST, PLATFORM_LN882X, + PLATFORM_NRF52, PLATFORM_RP2040, PLATFORM_RTL87XX, ) @@ -670,6 +671,10 @@ class EsphomeCore: def is_libretiny(self): return self.is_bk72xx or self.is_rtl87xx or self.is_ln882x + @property + def is_nrf52(self): + return self.target_platform == PLATFORM_NRF52 + @property def is_host(self): return self.target_platform == PLATFORM_HOST @@ -686,6 +691,10 @@ class EsphomeCore: def using_esp_idf(self): return self.target_framework == "esp-idf" + @property + def using_zephyr(self): + return self.target_framework == "zephyr" + def add_job(self, func, *args, **kwargs) -> None: self.event_loop.add_job(func, *args, **kwargs) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index c3b404ae60..488ea3cdb3 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -678,7 +679,7 @@ class InterruptLock { ~InterruptLock(); protected: -#if defined(USE_ESP8266) || defined(USE_RP2040) +#if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR) uint32_t state_; #endif }; diff --git a/tests/components/gpio/test.nrf52-adafruit.yaml b/tests/components/gpio/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..3ca285117d --- /dev/null +++ b/tests/components/gpio/test.nrf52-adafruit.yaml @@ -0,0 +1,14 @@ +binary_sensor: + - platform: gpio + pin: 2 + id: gpio_binary_sensor + +output: + - platform: gpio + pin: 3 + id: gpio_output + +switch: + - platform: gpio + pin: 4 + id: gpio_switch diff --git a/tests/components/gpio/test.nrf52-mcumgr.yaml b/tests/components/gpio/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..3ca285117d --- /dev/null +++ b/tests/components/gpio/test.nrf52-mcumgr.yaml @@ -0,0 +1,14 @@ +binary_sensor: + - platform: gpio + pin: 2 + id: gpio_binary_sensor + +output: + - platform: gpio + pin: 3 + id: gpio_output + +switch: + - platform: gpio + pin: 4 + id: gpio_switch diff --git a/tests/components/logger/test.nrf52-adafruit.yaml b/tests/components/logger/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..70b485daac --- /dev/null +++ b/tests/components/logger/test.nrf52-adafruit.yaml @@ -0,0 +1,7 @@ +esphome: + on_boot: + then: + - logger.log: Hello world + +logger: + level: DEBUG diff --git a/tests/components/logger/test.nrf52-mcumgr.yaml b/tests/components/logger/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..70b485daac --- /dev/null +++ b/tests/components/logger/test.nrf52-mcumgr.yaml @@ -0,0 +1,7 @@ +esphome: + on_boot: + then: + - logger.log: Hello world + +logger: + level: DEBUG diff --git a/tests/components/time/test.nrf52-adafruit.yaml b/tests/components/time/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..a5502f8028 --- /dev/null +++ b/tests/components/time/test.nrf52-adafruit.yaml @@ -0,0 +1 @@ +time: diff --git a/tests/components/time/test.nrf52-mcumgr.yaml b/tests/components/time/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..a5502f8028 --- /dev/null +++ b/tests/components/time/test.nrf52-mcumgr.yaml @@ -0,0 +1 @@ +time: diff --git a/tests/components/uptime/test.nrf52-adafruit.yaml b/tests/components/uptime/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..3c3c814813 --- /dev/null +++ b/tests/components/uptime/test.nrf52-adafruit.yaml @@ -0,0 +1,10 @@ +sensor: + - platform: uptime + name: Uptime Sensor + - platform: uptime + name: Uptime Sensor Seconds + type: seconds + +text_sensor: + - platform: uptime + name: Uptime Text diff --git a/tests/components/uptime/test.nrf52-mcumgr.yaml b/tests/components/uptime/test.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..3c3c814813 --- /dev/null +++ b/tests/components/uptime/test.nrf52-mcumgr.yaml @@ -0,0 +1,10 @@ +sensor: + - platform: uptime + name: Uptime Sensor + - platform: uptime + name: Uptime Sensor Seconds + type: seconds + +text_sensor: + - platform: uptime + name: Uptime Text diff --git a/tests/test_build_components/build_components_base.nrf52-adafruit.yaml b/tests/test_build_components/build_components_base.nrf52-adafruit.yaml new file mode 100644 index 0000000000..05e3a6387c --- /dev/null +++ b/tests/test_build_components/build_components_base.nrf52-adafruit.yaml @@ -0,0 +1,16 @@ +esphome: + name: componenttestnrf52 + friendly_name: $component_name + +nrf52: + board: adafruit_itsybitsy_nrf52840 + bootloader: adafruit_nrf52_sd140_v6 + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file diff --git a/tests/test_build_components/build_components_base.nrf52-mcumgr.yaml b/tests/test_build_components/build_components_base.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..04211ffdfe --- /dev/null +++ b/tests/test_build_components/build_components_base.nrf52-mcumgr.yaml @@ -0,0 +1,15 @@ +esphome: + name: componenttestnrf52 + friendly_name: $component_name + +nrf52: + board: adafruit_feather_nrf52840 + +logger: + level: VERY_VERBOSE + +packages: + component_under_test: !include + file: $component_test_file + vars: + component_test_file: $component_test_file From 6ab3de65a6c6914f11385f4a1c26c301caab34f2 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 16 Jul 2025 03:02:14 +0200 Subject: [PATCH 88/90] remove duplication from component_iterator (#7210) Co-authored-by: Samuel Tardieu Co-authored-by: J. Nick Koston --- esphome/components/api/user_services.h | 2 + esphome/core/component_iterator.cpp | 335 ++++++------------------- esphome/core/component_iterator.h | 5 + 3 files changed, 81 insertions(+), 261 deletions(-) diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 93cea8133f..1420a15ff9 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -16,6 +16,8 @@ class UserServiceDescriptor { virtual ListEntitiesServicesResponse encode_list_service_response() = 0; virtual bool execute_service(const ExecuteServiceRequest &req) = 0; + + bool is_internal() { return false; } }; template T get_execute_arg_value(const ExecuteServiceArgument &arg); diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index d27c4e70ba..1e8f670d8b 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -16,373 +16,186 @@ void ComponentIterator::begin(bool include_internal) { this->at_ = 0; this->include_internal_ = include_internal; } + +template +void ComponentIterator::process_platform_item_(const std::vector &items, + bool (ComponentIterator::*on_item)(PlatformItem *)) { + if (this->at_ >= items.size()) { + this->advance_platform_(); + } else { + PlatformItem *item = items[this->at_]; + if ((item->is_internal() && !this->include_internal_) || (this->*on_item)(item)) { + this->at_++; + } + } +} + +void ComponentIterator::advance_platform_() { + this->state_ = static_cast(static_cast(this->state_) + 1); + this->at_ = 0; +} + void ComponentIterator::advance() { - bool advance_platform = false; - bool success = true; switch (this->state_) { case IteratorState::NONE: // not started return; case IteratorState::BEGIN: if (this->on_begin()) { - advance_platform = true; - } else { - return; + advance_platform_(); } break; + #ifdef USE_BINARY_SENSOR case IteratorState::BINARY_SENSOR: - if (this->at_ >= App.get_binary_sensors().size()) { - advance_platform = true; - } else { - auto *binary_sensor = App.get_binary_sensors()[this->at_]; - if (binary_sensor->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_binary_sensor(binary_sensor); - } - } + this->process_platform_item_(App.get_binary_sensors(), &ComponentIterator::on_binary_sensor); break; #endif + #ifdef USE_COVER case IteratorState::COVER: - if (this->at_ >= App.get_covers().size()) { - advance_platform = true; - } else { - auto *cover = App.get_covers()[this->at_]; - if (cover->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_cover(cover); - } - } + this->process_platform_item_(App.get_covers(), &ComponentIterator::on_cover); break; #endif + #ifdef USE_FAN case IteratorState::FAN: - if (this->at_ >= App.get_fans().size()) { - advance_platform = true; - } else { - auto *fan = App.get_fans()[this->at_]; - if (fan->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_fan(fan); - } - } + this->process_platform_item_(App.get_fans(), &ComponentIterator::on_fan); break; #endif + #ifdef USE_LIGHT case IteratorState::LIGHT: - if (this->at_ >= App.get_lights().size()) { - advance_platform = true; - } else { - auto *light = App.get_lights()[this->at_]; - if (light->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_light(light); - } - } + this->process_platform_item_(App.get_lights(), &ComponentIterator::on_light); break; #endif + #ifdef USE_SENSOR case IteratorState::SENSOR: - if (this->at_ >= App.get_sensors().size()) { - advance_platform = true; - } else { - auto *sensor = App.get_sensors()[this->at_]; - if (sensor->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_sensor(sensor); - } - } + this->process_platform_item_(App.get_sensors(), &ComponentIterator::on_sensor); break; #endif + #ifdef USE_SWITCH case IteratorState::SWITCH: - if (this->at_ >= App.get_switches().size()) { - advance_platform = true; - } else { - auto *a_switch = App.get_switches()[this->at_]; - if (a_switch->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_switch(a_switch); - } - } + this->process_platform_item_(App.get_switches(), &ComponentIterator::on_switch); break; #endif + #ifdef USE_BUTTON case IteratorState::BUTTON: - if (this->at_ >= App.get_buttons().size()) { - advance_platform = true; - } else { - auto *button = App.get_buttons()[this->at_]; - if (button->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_button(button); - } - } + this->process_platform_item_(App.get_buttons(), &ComponentIterator::on_button); break; #endif + #ifdef USE_TEXT_SENSOR case IteratorState::TEXT_SENSOR: - if (this->at_ >= App.get_text_sensors().size()) { - advance_platform = true; - } else { - auto *text_sensor = App.get_text_sensors()[this->at_]; - if (text_sensor->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_text_sensor(text_sensor); - } - } + this->process_platform_item_(App.get_text_sensors(), &ComponentIterator::on_text_sensor); break; #endif + #ifdef USE_API_SERVICES - case IteratorState ::SERVICE: - if (this->at_ >= api::global_api_server->get_user_services().size()) { - advance_platform = true; - } else { - auto *service = api::global_api_server->get_user_services()[this->at_]; - success = this->on_service(service); - } + case IteratorState::SERVICE: + this->process_platform_item_(api::global_api_server->get_user_services(), &ComponentIterator::on_service); break; #endif + #ifdef USE_CAMERA - case IteratorState::CAMERA: - if (camera::Camera::instance() == nullptr) { - advance_platform = true; - } else { - if (camera::Camera::instance()->is_internal() && !this->include_internal_) { - advance_platform = success = true; - break; - } else { - advance_platform = success = this->on_camera(camera::Camera::instance()); - } + case IteratorState::CAMERA: { + camera::Camera *camera_instance = camera::Camera::instance(); + if (camera_instance != nullptr && (!camera_instance->is_internal() || this->include_internal_)) { + this->on_camera(camera_instance); } - break; + advance_platform_(); + } break; #endif + #ifdef USE_CLIMATE case IteratorState::CLIMATE: - if (this->at_ >= App.get_climates().size()) { - advance_platform = true; - } else { - auto *climate = App.get_climates()[this->at_]; - if (climate->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_climate(climate); - } - } + this->process_platform_item_(App.get_climates(), &ComponentIterator::on_climate); break; #endif + #ifdef USE_NUMBER case IteratorState::NUMBER: - if (this->at_ >= App.get_numbers().size()) { - advance_platform = true; - } else { - auto *number = App.get_numbers()[this->at_]; - if (number->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_number(number); - } - } + this->process_platform_item_(App.get_numbers(), &ComponentIterator::on_number); break; #endif + #ifdef USE_DATETIME_DATE case IteratorState::DATETIME_DATE: - if (this->at_ >= App.get_dates().size()) { - advance_platform = true; - } else { - auto *date = App.get_dates()[this->at_]; - if (date->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_date(date); - } - } + this->process_platform_item_(App.get_dates(), &ComponentIterator::on_date); break; #endif + #ifdef USE_DATETIME_TIME case IteratorState::DATETIME_TIME: - if (this->at_ >= App.get_times().size()) { - advance_platform = true; - } else { - auto *time = App.get_times()[this->at_]; - if (time->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_time(time); - } - } + this->process_platform_item_(App.get_times(), &ComponentIterator::on_time); break; #endif + #ifdef USE_DATETIME_DATETIME case IteratorState::DATETIME_DATETIME: - if (this->at_ >= App.get_datetimes().size()) { - advance_platform = true; - } else { - auto *datetime = App.get_datetimes()[this->at_]; - if (datetime->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_datetime(datetime); - } - } + this->process_platform_item_(App.get_datetimes(), &ComponentIterator::on_datetime); break; #endif + #ifdef USE_TEXT case IteratorState::TEXT: - if (this->at_ >= App.get_texts().size()) { - advance_platform = true; - } else { - auto *text = App.get_texts()[this->at_]; - if (text->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_text(text); - } - } + this->process_platform_item_(App.get_texts(), &ComponentIterator::on_text); break; #endif + #ifdef USE_SELECT case IteratorState::SELECT: - if (this->at_ >= App.get_selects().size()) { - advance_platform = true; - } else { - auto *select = App.get_selects()[this->at_]; - if (select->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_select(select); - } - } + this->process_platform_item_(App.get_selects(), &ComponentIterator::on_select); break; #endif + #ifdef USE_LOCK case IteratorState::LOCK: - if (this->at_ >= App.get_locks().size()) { - advance_platform = true; - } else { - auto *a_lock = App.get_locks()[this->at_]; - if (a_lock->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_lock(a_lock); - } - } + this->process_platform_item_(App.get_locks(), &ComponentIterator::on_lock); break; #endif + #ifdef USE_VALVE case IteratorState::VALVE: - if (this->at_ >= App.get_valves().size()) { - advance_platform = true; - } else { - auto *valve = App.get_valves()[this->at_]; - if (valve->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_valve(valve); - } - } + this->process_platform_item_(App.get_valves(), &ComponentIterator::on_valve); break; #endif + #ifdef USE_MEDIA_PLAYER case IteratorState::MEDIA_PLAYER: - if (this->at_ >= App.get_media_players().size()) { - advance_platform = true; - } else { - auto *media_player = App.get_media_players()[this->at_]; - if (media_player->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_media_player(media_player); - } - } + this->process_platform_item_(App.get_media_players(), &ComponentIterator::on_media_player); break; #endif + #ifdef USE_ALARM_CONTROL_PANEL case IteratorState::ALARM_CONTROL_PANEL: - if (this->at_ >= App.get_alarm_control_panels().size()) { - advance_platform = true; - } else { - auto *a_alarm_control_panel = App.get_alarm_control_panels()[this->at_]; - if (a_alarm_control_panel->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_alarm_control_panel(a_alarm_control_panel); - } - } + this->process_platform_item_(App.get_alarm_control_panels(), &ComponentIterator::on_alarm_control_panel); break; #endif + #ifdef USE_EVENT case IteratorState::EVENT: - if (this->at_ >= App.get_events().size()) { - advance_platform = true; - } else { - auto *event = App.get_events()[this->at_]; - if (event->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_event(event); - } - } + this->process_platform_item_(App.get_events(), &ComponentIterator::on_event); break; #endif + #ifdef USE_UPDATE case IteratorState::UPDATE: - if (this->at_ >= App.get_updates().size()) { - advance_platform = true; - } else { - auto *update = App.get_updates()[this->at_]; - if (update->is_internal() && !this->include_internal_) { - success = true; - break; - } else { - success = this->on_update(update); - } - } + this->process_platform_item_(App.get_updates(), &ComponentIterator::on_update); break; #endif + case IteratorState::MAX: if (this->on_end()) { this->state_ = IteratorState::NONE; } return; } - - if (advance_platform) { - this->state_ = static_cast(static_cast(this->state_) + 1); - this->at_ = 0; - } else if (success) { - this->at_++; - } } + bool ComponentIterator::on_end() { return true; } bool ComponentIterator::on_begin() { return true; } #ifdef USE_API_SERVICES diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index ea2c8004ac..7a9771b8f2 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -171,6 +171,11 @@ class ComponentIterator { } state_{IteratorState::NONE}; uint16_t at_{0}; // Supports up to 65,535 entities per type bool include_internal_{false}; + + template + void process_platform_item_(const std::vector &items, + bool (ComponentIterator::*on_item)(PlatformItem *)); + void advance_platform_(); }; } // namespace esphome From 5480675dd8c43e42ff2be3b7ffead65b18351da8 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 16 Jul 2025 03:03:19 +0200 Subject: [PATCH 89/90] [adc] Use new library with ESP-IDF v5 (#9021) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/adc/__init__.py | 169 ++++---- esphome/components/adc/adc_sensor.h | 117 ++++-- esphome/components/adc/adc_sensor_esp32.cpp | 417 ++++++++++++++------ esphome/components/adc/sensor.py | 36 +- 4 files changed, 469 insertions(+), 270 deletions(-) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 10b7df8638..e1cb6a9e01 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -51,82 +51,83 @@ SAMPLING_MODES = { "max": sampling_mode.MAX, } -adc1_channel_t = cg.global_ns.enum("adc1_channel_t") -adc2_channel_t = cg.global_ns.enum("adc2_channel_t") +adc_unit_t = cg.global_ns.enum("adc_unit_t", is_class=True) + +adc_channel_t = cg.global_ns.enum("adc_channel_t", is_class=True) # pin to adc1 channel mapping # https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h VARIANT_ESP32: { - 36: adc1_channel_t.ADC1_CHANNEL_0, - 37: adc1_channel_t.ADC1_CHANNEL_1, - 38: adc1_channel_t.ADC1_CHANNEL_2, - 39: adc1_channel_t.ADC1_CHANNEL_3, - 32: adc1_channel_t.ADC1_CHANNEL_4, - 33: adc1_channel_t.ADC1_CHANNEL_5, - 34: adc1_channel_t.ADC1_CHANNEL_6, - 35: adc1_channel_t.ADC1_CHANNEL_7, + 36: adc_channel_t.ADC_CHANNEL_0, + 37: adc_channel_t.ADC_CHANNEL_1, + 38: adc_channel_t.ADC_CHANNEL_2, + 39: adc_channel_t.ADC_CHANNEL_3, + 32: adc_channel_t.ADC_CHANNEL_4, + 33: adc_channel_t.ADC_CHANNEL_5, + 34: adc_channel_t.ADC_CHANNEL_6, + 35: adc_channel_t.ADC_CHANNEL_7, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h VARIANT_ESP32C2: { - 0: adc1_channel_t.ADC1_CHANNEL_0, - 1: adc1_channel_t.ADC1_CHANNEL_1, - 2: adc1_channel_t.ADC1_CHANNEL_2, - 3: adc1_channel_t.ADC1_CHANNEL_3, - 4: adc1_channel_t.ADC1_CHANNEL_4, + 0: adc_channel_t.ADC_CHANNEL_0, + 1: adc_channel_t.ADC_CHANNEL_1, + 2: adc_channel_t.ADC_CHANNEL_2, + 3: adc_channel_t.ADC_CHANNEL_3, + 4: adc_channel_t.ADC_CHANNEL_4, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h VARIANT_ESP32C3: { - 0: adc1_channel_t.ADC1_CHANNEL_0, - 1: adc1_channel_t.ADC1_CHANNEL_1, - 2: adc1_channel_t.ADC1_CHANNEL_2, - 3: adc1_channel_t.ADC1_CHANNEL_3, - 4: adc1_channel_t.ADC1_CHANNEL_4, + 0: adc_channel_t.ADC_CHANNEL_0, + 1: adc_channel_t.ADC_CHANNEL_1, + 2: adc_channel_t.ADC_CHANNEL_2, + 3: adc_channel_t.ADC_CHANNEL_3, + 4: adc_channel_t.ADC_CHANNEL_4, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h VARIANT_ESP32C6: { - 0: adc1_channel_t.ADC1_CHANNEL_0, - 1: adc1_channel_t.ADC1_CHANNEL_1, - 2: adc1_channel_t.ADC1_CHANNEL_2, - 3: adc1_channel_t.ADC1_CHANNEL_3, - 4: adc1_channel_t.ADC1_CHANNEL_4, - 5: adc1_channel_t.ADC1_CHANNEL_5, - 6: adc1_channel_t.ADC1_CHANNEL_6, + 0: adc_channel_t.ADC_CHANNEL_0, + 1: adc_channel_t.ADC_CHANNEL_1, + 2: adc_channel_t.ADC_CHANNEL_2, + 3: adc_channel_t.ADC_CHANNEL_3, + 4: adc_channel_t.ADC_CHANNEL_4, + 5: adc_channel_t.ADC_CHANNEL_5, + 6: adc_channel_t.ADC_CHANNEL_6, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h VARIANT_ESP32H2: { - 1: adc1_channel_t.ADC1_CHANNEL_0, - 2: adc1_channel_t.ADC1_CHANNEL_1, - 3: adc1_channel_t.ADC1_CHANNEL_2, - 4: adc1_channel_t.ADC1_CHANNEL_3, - 5: adc1_channel_t.ADC1_CHANNEL_4, + 1: adc_channel_t.ADC_CHANNEL_0, + 2: adc_channel_t.ADC_CHANNEL_1, + 3: adc_channel_t.ADC_CHANNEL_2, + 4: adc_channel_t.ADC_CHANNEL_3, + 5: adc_channel_t.ADC_CHANNEL_4, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h VARIANT_ESP32S2: { - 1: adc1_channel_t.ADC1_CHANNEL_0, - 2: adc1_channel_t.ADC1_CHANNEL_1, - 3: adc1_channel_t.ADC1_CHANNEL_2, - 4: adc1_channel_t.ADC1_CHANNEL_3, - 5: adc1_channel_t.ADC1_CHANNEL_4, - 6: adc1_channel_t.ADC1_CHANNEL_5, - 7: adc1_channel_t.ADC1_CHANNEL_6, - 8: adc1_channel_t.ADC1_CHANNEL_7, - 9: adc1_channel_t.ADC1_CHANNEL_8, - 10: adc1_channel_t.ADC1_CHANNEL_9, + 1: adc_channel_t.ADC_CHANNEL_0, + 2: adc_channel_t.ADC_CHANNEL_1, + 3: adc_channel_t.ADC_CHANNEL_2, + 4: adc_channel_t.ADC_CHANNEL_3, + 5: adc_channel_t.ADC_CHANNEL_4, + 6: adc_channel_t.ADC_CHANNEL_5, + 7: adc_channel_t.ADC_CHANNEL_6, + 8: adc_channel_t.ADC_CHANNEL_7, + 9: adc_channel_t.ADC_CHANNEL_8, + 10: adc_channel_t.ADC_CHANNEL_9, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h VARIANT_ESP32S3: { - 1: adc1_channel_t.ADC1_CHANNEL_0, - 2: adc1_channel_t.ADC1_CHANNEL_1, - 3: adc1_channel_t.ADC1_CHANNEL_2, - 4: adc1_channel_t.ADC1_CHANNEL_3, - 5: adc1_channel_t.ADC1_CHANNEL_4, - 6: adc1_channel_t.ADC1_CHANNEL_5, - 7: adc1_channel_t.ADC1_CHANNEL_6, - 8: adc1_channel_t.ADC1_CHANNEL_7, - 9: adc1_channel_t.ADC1_CHANNEL_8, - 10: adc1_channel_t.ADC1_CHANNEL_9, + 1: adc_channel_t.ADC_CHANNEL_0, + 2: adc_channel_t.ADC_CHANNEL_1, + 3: adc_channel_t.ADC_CHANNEL_2, + 4: adc_channel_t.ADC_CHANNEL_3, + 5: adc_channel_t.ADC_CHANNEL_4, + 6: adc_channel_t.ADC_CHANNEL_5, + 7: adc_channel_t.ADC_CHANNEL_6, + 8: adc_channel_t.ADC_CHANNEL_7, + 9: adc_channel_t.ADC_CHANNEL_8, + 10: adc_channel_t.ADC_CHANNEL_9, }, } @@ -135,24 +136,24 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = { # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h VARIANT_ESP32: { - 4: adc2_channel_t.ADC2_CHANNEL_0, - 0: adc2_channel_t.ADC2_CHANNEL_1, - 2: adc2_channel_t.ADC2_CHANNEL_2, - 15: adc2_channel_t.ADC2_CHANNEL_3, - 13: adc2_channel_t.ADC2_CHANNEL_4, - 12: adc2_channel_t.ADC2_CHANNEL_5, - 14: adc2_channel_t.ADC2_CHANNEL_6, - 27: adc2_channel_t.ADC2_CHANNEL_7, - 25: adc2_channel_t.ADC2_CHANNEL_8, - 26: adc2_channel_t.ADC2_CHANNEL_9, + 4: adc_channel_t.ADC_CHANNEL_0, + 0: adc_channel_t.ADC_CHANNEL_1, + 2: adc_channel_t.ADC_CHANNEL_2, + 15: adc_channel_t.ADC_CHANNEL_3, + 13: adc_channel_t.ADC_CHANNEL_4, + 12: adc_channel_t.ADC_CHANNEL_5, + 14: adc_channel_t.ADC_CHANNEL_6, + 27: adc_channel_t.ADC_CHANNEL_7, + 25: adc_channel_t.ADC_CHANNEL_8, + 26: adc_channel_t.ADC_CHANNEL_9, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h VARIANT_ESP32C2: { - 5: adc2_channel_t.ADC2_CHANNEL_0, + 5: adc_channel_t.ADC_CHANNEL_0, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h VARIANT_ESP32C3: { - 5: adc2_channel_t.ADC2_CHANNEL_0, + 5: adc_channel_t.ADC_CHANNEL_0, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h VARIANT_ESP32C6: {}, # no ADC2 @@ -160,29 +161,29 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = { VARIANT_ESP32H2: {}, # no ADC2 # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h VARIANT_ESP32S2: { - 11: adc2_channel_t.ADC2_CHANNEL_0, - 12: adc2_channel_t.ADC2_CHANNEL_1, - 13: adc2_channel_t.ADC2_CHANNEL_2, - 14: adc2_channel_t.ADC2_CHANNEL_3, - 15: adc2_channel_t.ADC2_CHANNEL_4, - 16: adc2_channel_t.ADC2_CHANNEL_5, - 17: adc2_channel_t.ADC2_CHANNEL_6, - 18: adc2_channel_t.ADC2_CHANNEL_7, - 19: adc2_channel_t.ADC2_CHANNEL_8, - 20: adc2_channel_t.ADC2_CHANNEL_9, + 11: adc_channel_t.ADC_CHANNEL_0, + 12: adc_channel_t.ADC_CHANNEL_1, + 13: adc_channel_t.ADC_CHANNEL_2, + 14: adc_channel_t.ADC_CHANNEL_3, + 15: adc_channel_t.ADC_CHANNEL_4, + 16: adc_channel_t.ADC_CHANNEL_5, + 17: adc_channel_t.ADC_CHANNEL_6, + 18: adc_channel_t.ADC_CHANNEL_7, + 19: adc_channel_t.ADC_CHANNEL_8, + 20: adc_channel_t.ADC_CHANNEL_9, }, # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h VARIANT_ESP32S3: { - 11: adc2_channel_t.ADC2_CHANNEL_0, - 12: adc2_channel_t.ADC2_CHANNEL_1, - 13: adc2_channel_t.ADC2_CHANNEL_2, - 14: adc2_channel_t.ADC2_CHANNEL_3, - 15: adc2_channel_t.ADC2_CHANNEL_4, - 16: adc2_channel_t.ADC2_CHANNEL_5, - 17: adc2_channel_t.ADC2_CHANNEL_6, - 18: adc2_channel_t.ADC2_CHANNEL_7, - 19: adc2_channel_t.ADC2_CHANNEL_8, - 20: adc2_channel_t.ADC2_CHANNEL_9, + 11: adc_channel_t.ADC_CHANNEL_0, + 12: adc_channel_t.ADC_CHANNEL_1, + 13: adc_channel_t.ADC_CHANNEL_2, + 14: adc_channel_t.ADC_CHANNEL_3, + 15: adc_channel_t.ADC_CHANNEL_4, + 16: adc_channel_t.ADC_CHANNEL_5, + 17: adc_channel_t.ADC_CHANNEL_6, + 18: adc_channel_t.ADC_CHANNEL_7, + 19: adc_channel_t.ADC_CHANNEL_8, + 20: adc_channel_t.ADC_CHANNEL_9, }, } diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 28dfd2262c..7b1f69e454 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -3,12 +3,15 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/hal.h" #ifdef USE_ESP32 -#include -#include "driver/adc.h" -#endif // USE_ESP32 +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include "esp_adc/adc_oneshot.h" +#include "hal/adc_types.h" // This defines ADC_CHANNEL_MAX +#endif // USE_ESP32 namespace esphome { namespace adc { @@ -49,33 +52,72 @@ class Aggregator { class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: + /// Update the sensor's state by reading the current ADC value. + /// This method is called periodically based on the update interval. + void update() override; + + /// Set up the ADC sensor by initializing hardware and calibration parameters. + /// This method is called once during device initialization. + void setup() override; + + /// Output the configuration details of the ADC sensor for debugging purposes. + /// This method is called during the ESPHome setup process to log the configuration. + void dump_config() override; + + /// Return the setup priority for this component. + /// Components with higher priority are initialized earlier during setup. + /// @return A float representing the setup priority. + float get_setup_priority() const override; + + /// Set the GPIO pin to be used by the ADC sensor. + /// @param pin Pointer to an InternalGPIOPin representing the ADC input pin. + void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } + + /// Enable or disable the output of raw ADC values (unprocessed data). + /// @param output_raw Boolean indicating whether to output raw ADC values (true) or processed values (false). + void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; } + + /// Set the number of samples to be taken for ADC readings to improve accuracy. + /// A higher sample count reduces noise but increases the reading time. + /// @param sample_count The number of samples (e.g., 1, 4, 8). + void set_sample_count(uint8_t sample_count); + + /// Set the sampling mode for how multiple ADC samples are combined into a single measurement. + /// + /// When multiple samples are taken (controlled by set_sample_count), they can be combined + /// in one of three ways: + /// - SamplingMode::AVG: Compute the average (default) + /// - SamplingMode::MIN: Use the lowest sample value + /// - SamplingMode::MAX: Use the highest sample value + /// @param sampling_mode The desired sampling mode to use for aggregating ADC samples. + void set_sampling_mode(SamplingMode sampling_mode); + + /// Perform a single ADC sampling operation and return the measured value. + /// This function handles raw readings, calibration, and averaging as needed. + /// @return The sampled value as a float. + float sample() override; + #ifdef USE_ESP32 - /// Set the attenuation for this pin. Only available on the ESP32. + /// Set the ADC attenuation level to adjust the input voltage range. + /// This determines how the ADC interprets input voltages, allowing for greater precision + /// or the ability to measure higher voltages depending on the chosen attenuation level. + /// @param attenuation The desired ADC attenuation level (e.g., ADC_ATTEN_DB_0, ADC_ATTEN_DB_11). void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; } - void set_channel1(adc1_channel_t channel) { - this->channel1_ = channel; - this->channel2_ = ADC2_CHANNEL_MAX; - } - void set_channel2(adc2_channel_t channel) { - this->channel2_ = channel; - this->channel1_ = ADC1_CHANNEL_MAX; + + /// Configure the ADC to use a specific channel on ADC1. + /// This sets the channel for single-shot or continuous ADC measurements. + /// @param channel The ADC1 channel to configure, such as ADC_CHANNEL_0, ADC_CHANNEL_3, etc. + void set_channel(adc_unit_t unit, adc_channel_t channel) { + this->adc_unit_ = unit; + this->channel_ = channel; } + + /// Set whether autoranging should be enabled for the ADC. + /// Autoranging automatically adjusts the attenuation level to handle a wide range of input voltages. + /// @param autorange Boolean indicating whether to enable autoranging. void set_autorange(bool autorange) { this->autorange_ = autorange; } #endif // USE_ESP32 - /// Update ADC values - void update() override; - /// Setup ADC - void setup() override; - void dump_config() override; - /// `HARDWARE_LATE` setup priority - float get_setup_priority() const override; - void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } - void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; } - void set_sample_count(uint8_t sample_count); - void set_sampling_mode(SamplingMode sampling_mode); - float sample() override; - #ifdef USE_ESP8266 std::string unique_id() override; #endif // USE_ESP8266 @@ -90,17 +132,28 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage InternalGPIOPin *pin_; SamplingMode sampling_mode_{SamplingMode::AVG}; +#ifdef USE_ESP32 + float sample_autorange_(); + float sample_fixed_attenuation_(); + bool autorange_{false}; + adc_oneshot_unit_handle_t adc_handle_{nullptr}; + adc_cali_handle_t calibration_handle_{nullptr}; + adc_atten_t attenuation_{ADC_ATTEN_DB_0}; + adc_channel_t channel_; + adc_unit_t adc_unit_; + struct SetupFlags { + uint8_t init_complete : 1; + uint8_t config_complete : 1; + uint8_t handle_init_complete : 1; + uint8_t calibration_complete : 1; + uint8_t reserved : 4; + } setup_flags_{}; + static adc_oneshot_unit_handle_t shared_adc_handles[2]; +#endif // USE_ESP32 + #ifdef USE_RP2040 bool is_temperature_{false}; #endif // USE_RP2040 - -#ifdef USE_ESP32 - adc_atten_t attenuation_{ADC_ATTEN_DB_0}; - adc1_channel_t channel1_{ADC1_CHANNEL_MAX}; - adc2_channel_t channel2_{ADC2_CHANNEL_MAX}; - bool autorange_{false}; - esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {}; -#endif // USE_ESP32 }; } // namespace adc diff --git a/esphome/components/adc/adc_sensor_esp32.cpp b/esphome/components/adc/adc_sensor_esp32.cpp index ed1f3329ab..f38d339304 100644 --- a/esphome/components/adc/adc_sensor_esp32.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -8,145 +8,308 @@ namespace adc { static const char *const TAG = "adc.esp32"; -static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast(ADC_WIDTH_MAX - 1); +adc_oneshot_unit_handle_t ADCSensor::shared_adc_handles[2] = {nullptr, nullptr}; -#ifndef SOC_ADC_RTC_MAX_BITWIDTH -#if USE_ESP32_VARIANT_ESP32S2 -static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13; -#else -static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12; -#endif // USE_ESP32_VARIANT_ESP32S2 -#endif // SOC_ADC_RTC_MAX_BITWIDTH - -static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; -static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; - -void ADCSensor::setup() { - ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); - - if (this->channel1_ != ADC1_CHANNEL_MAX) { - adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); - if (!this->autorange_) { - adc1_config_channel_atten(this->channel1_, this->attenuation_); - } - } else if (this->channel2_ != ADC2_CHANNEL_MAX) { - if (!this->autorange_) { - adc2_config_channel_atten(this->channel2_, this->attenuation_); - } - } - - for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) { - auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; - auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS, - 1100, // default vref - &this->cal_characteristics_[i]); - switch (cal_value) { - case ESP_ADC_CAL_VAL_EFUSE_VREF: - ESP_LOGV(TAG, "Using eFuse Vref for calibration"); - break; - case ESP_ADC_CAL_VAL_EFUSE_TP: - ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration"); - break; - case ESP_ADC_CAL_VAL_DEFAULT_VREF: - default: - break; - } +const LogString *attenuation_to_str(adc_atten_t attenuation) { + switch (attenuation) { + case ADC_ATTEN_DB_0: + return LOG_STR("0 dB"); + case ADC_ATTEN_DB_2_5: + return LOG_STR("2.5 dB"); + case ADC_ATTEN_DB_6: + return LOG_STR("6 dB"); + case ADC_ATTEN_DB_12_COMPAT: + return LOG_STR("12 dB"); + default: + return LOG_STR("Unknown Attenuation"); } } -void ADCSensor::dump_config() { - static const char *const ATTEN_AUTO_STR = "auto"; - static const char *const ATTEN_0DB_STR = "0 db"; - static const char *const ATTEN_2_5DB_STR = "2.5 db"; - static const char *const ATTEN_6DB_STR = "6 db"; - static const char *const ATTEN_12DB_STR = "12 db"; - const char *atten_str = ATTEN_AUTO_STR; +const LogString *adc_unit_to_str(adc_unit_t unit) { + switch (unit) { + case ADC_UNIT_1: + return LOG_STR("ADC1"); + case ADC_UNIT_2: + return LOG_STR("ADC2"); + default: + return LOG_STR("Unknown ADC Unit"); + } +} - LOG_SENSOR("", "ADC Sensor", this); - LOG_PIN(" Pin: ", this->pin_); - - if (!this->autorange_) { - switch (this->attenuation_) { - case ADC_ATTEN_DB_0: - atten_str = ATTEN_0DB_STR; - break; - case ADC_ATTEN_DB_2_5: - atten_str = ATTEN_2_5DB_STR; - break; - case ADC_ATTEN_DB_6: - atten_str = ATTEN_6DB_STR; - break; - case ADC_ATTEN_DB_12_COMPAT: - atten_str = ATTEN_12DB_STR; - break; - default: // This is to satisfy the unused ADC_ATTEN_MAX - break; +void ADCSensor::setup() { + ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str()); + // Check if another sensor already initialized this ADC unit + if (ADCSensor::shared_adc_handles[this->adc_unit_] == nullptr) { + adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize + init_config.unit_id = this->adc_unit_; + init_config.ulp_mode = ADC_ULP_MODE_DISABLE; +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 + init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT; +#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 + esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err); + this->mark_failed(); + return; } } + this->adc_handle_ = ADCSensor::shared_adc_handles[this->adc_unit_]; + this->setup_flags_.handle_init_complete = true; + + adc_oneshot_chan_cfg_t config = { + .atten = this->attenuation_, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error configuring channel: %d", err); + this->mark_failed(); + return; + } + this->setup_flags_.config_complete = true; + + // Initialize ADC calibration + if (this->calibration_handle_ == nullptr) { + adc_cali_handle_t handle = nullptr; + esp_err_t err; + +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + // RISC-V variants and S3 use curve fitting calibration + adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + cali_config.chan = this->channel_; +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + cali_config.unit_id = this->adc_unit_; + cali_config.atten = this->attenuation_; + cali_config.bitwidth = ADC_BITWIDTH_DEFAULT; + + err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); + if (err == ESP_OK) { + this->calibration_handle_ = handle; + this->setup_flags_.calibration_complete = true; + ESP_LOGV(TAG, "Using curve fitting calibration"); + } else { + ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err); + this->setup_flags_.calibration_complete = false; + } +#else // Other ESP32 variants use line fitting calibration + adc_cali_line_fitting_config_t cali_config = { + .unit_id = this->adc_unit_, + .atten = this->attenuation_, + .bitwidth = ADC_BITWIDTH_DEFAULT, +#if !defined(USE_ESP32_VARIANT_ESP32S2) + .default_vref = 1100, // Default reference voltage in mV +#endif // !defined(USE_ESP32_VARIANT_ESP32S2) + }; + err = adc_cali_create_scheme_line_fitting(&cali_config, &handle); + if (err == ESP_OK) { + this->calibration_handle_ = handle; + this->setup_flags_.calibration_complete = true; + ESP_LOGV(TAG, "Using line fitting calibration"); + } else { + ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err); + this->setup_flags_.calibration_complete = false; + } +#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2 + } + + this->setup_flags_.init_complete = true; +} + +void ADCSensor::dump_config() { + LOG_SENSOR("", "ADC Sensor", this); + LOG_PIN(" Pin: ", this->pin_); ESP_LOGCONFIG(TAG, - " Attenuation: %s\n" - " Samples: %i\n" + " Channel: %d\n" + " Unit: %s\n" + " Attenuation: %s\n" + " Samples: %i\n" " Sampling mode: %s", - atten_str, this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), + this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_, + LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); + + ESP_LOGCONFIG( + TAG, + " Setup Status:\n" + " Handle Init: %s\n" + " Config: %s\n" + " Calibration: %s\n" + " Overall Init: %s", + this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED", + this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED"); + LOG_UPDATE_INTERVAL(this); } float ADCSensor::sample() { - if (!this->autorange_) { - auto aggr = Aggregator(this->sampling_mode_); + if (this->autorange_) { + return this->sample_autorange_(); + } else { + return this->sample_fixed_attenuation_(); + } +} - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - int raw = -1; - if (this->channel1_ != ADC1_CHANNEL_MAX) { - raw = adc1_get_raw(this->channel1_); - } else if (this->channel2_ != ADC2_CHANNEL_MAX) { - adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw); - } - if (raw == -1) { - return NAN; - } +float ADCSensor::sample_fixed_attenuation_() { + auto aggr = Aggregator(this->sampling_mode_); - aggr.add_sample(raw); + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + int raw; + esp_err_t err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw); + + if (err != ESP_OK) { + ESP_LOGW(TAG, "ADC read failed with error %d", err); + continue; } - if (this->output_raw_) { - return aggr.aggregate(); + + if (raw == -1) { + ESP_LOGW(TAG, "Invalid ADC reading"); + continue; } - uint32_t mv = - esp_adc_cal_raw_to_voltage(aggr.aggregate(), &this->cal_characteristics_[(int32_t) this->attenuation_]); - return mv / 1000.0f; + + aggr.add_sample(raw); } - int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX; + uint32_t final_value = aggr.aggregate(); - if (this->channel1_ != ADC1_CHANNEL_MAX) { - adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT); - raw12 = adc1_get_raw(this->channel1_); - if (raw12 < ADC_MAX) { - adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6); - raw6 = adc1_get_raw(this->channel1_); - if (raw6 < ADC_MAX) { - adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5); - raw2 = adc1_get_raw(this->channel1_); - if (raw2 < ADC_MAX) { - adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0); - raw0 = adc1_get_raw(this->channel1_); - } + if (this->output_raw_) { + return final_value; + } + + if (this->calibration_handle_ != nullptr) { + int voltage_mv; + esp_err_t err = adc_cali_raw_to_voltage(this->calibration_handle_, final_value, &voltage_mv); + if (err == ESP_OK) { + return voltage_mv / 1000.0f; + } else { + ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err); + if (this->calibration_handle_ != nullptr) { +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_delete_scheme_curve_fitting(this->calibration_handle_); +#else // Other ESP32 variants use line fitting calibration + adc_cali_delete_scheme_line_fitting(this->calibration_handle_); +#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2 + this->calibration_handle_ = nullptr; } } - } else if (this->channel2_ != ADC2_CHANNEL_MAX) { - adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT); - adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12); - if (raw12 < ADC_MAX) { - adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6); - adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6); - if (raw6 < ADC_MAX) { - adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5); - adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2); - if (raw2 < ADC_MAX) { - adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0); - adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0); - } + } + + return final_value * 3.3f / 4095.0f; +} + +float ADCSensor::sample_autorange_() { + // Auto-range mode + auto read_atten = [this](adc_atten_t atten) -> std::pair { + // First reconfigure the attenuation for this reading + adc_oneshot_chan_cfg_t config = { + .atten = atten, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config); + + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error configuring ADC channel for autorange: %d", err); + return {-1, 0.0f}; + } + + // Need to recalibrate for the new attenuation + if (this->calibration_handle_ != nullptr) { + // Delete old calibration handle +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_delete_scheme_curve_fitting(this->calibration_handle_); +#else + adc_cali_delete_scheme_line_fitting(this->calibration_handle_); +#endif + this->calibration_handle_ = nullptr; + } + + // Create new calibration handle for this attenuation + adc_cali_handle_t handle = nullptr; + +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_curve_fitting_config_t cali_config = {}; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + cali_config.chan = this->channel_; +#endif + cali_config.unit_id = this->adc_unit_; + cali_config.atten = atten; + cali_config.bitwidth = ADC_BITWIDTH_DEFAULT; + + err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); +#else + adc_cali_line_fitting_config_t cali_config = { + .unit_id = this->adc_unit_, + .atten = atten, + .bitwidth = ADC_BITWIDTH_DEFAULT, +#if !defined(USE_ESP32_VARIANT_ESP32S2) + .default_vref = 1100, +#endif + }; + err = adc_cali_create_scheme_line_fitting(&cali_config, &handle); +#endif + + int raw; + err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw); + + if (err != ESP_OK) { + ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err); + if (handle != nullptr) { +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_delete_scheme_curve_fitting(handle); +#else + adc_cali_delete_scheme_line_fitting(handle); +#endif + } + return {-1, 0.0f}; + } + + float voltage = 0.0f; + if (handle != nullptr) { + int voltage_mv; + err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv); + if (err == ESP_OK) { + voltage = voltage_mv / 1000.0f; + } else { + voltage = raw * 3.3f / 4095.0f; + } + // Clean up calibration handle +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_delete_scheme_curve_fitting(handle); +#else + adc_cali_delete_scheme_line_fitting(handle); +#endif + } else { + voltage = raw * 3.3f / 4095.0f; + } + + return {raw, voltage}; + }; + + auto [raw12, mv12] = read_atten(ADC_ATTEN_DB_12); + if (raw12 == -1) { + ESP_LOGE(TAG, "Failed to read ADC in autorange mode"); + return NAN; + } + + int raw6 = 4095, raw2 = 4095, raw0 = 4095; + float mv6 = 0, mv2 = 0, mv0 = 0; + + if (raw12 < 4095) { + auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6); + raw6 = raw6_val; + mv6 = mv6_val; + + if (raw6 < 4095 && raw6 != -1) { + auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5); + raw2 = raw2_val; + mv2 = mv2_val; + + if (raw2 < 4095 && raw2 != -1) { + auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0); + raw0 = raw0_val; + mv0 = mv0_val; } } } @@ -155,19 +318,19 @@ float ADCSensor::sample() { return NAN; } - uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]); - uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]); - uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]); - uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]); - - uint32_t c12 = std::min(raw12, ADC_HALF); - uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); - uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); - uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); + const int adc_half = 2048; + uint32_t c12 = std::min(raw12, adc_half); + uint32_t c6 = adc_half - std::abs(raw6 - adc_half); + uint32_t c2 = adc_half - std::abs(raw2 - adc_half); + uint32_t c0 = std::min(4095 - raw0, adc_half); uint32_t csum = c12 + c6 + c2 + c0; - uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); - return mv_scaled / (float) (csum * 1000U); + if (csum == 0) { + ESP_LOGE(TAG, "Invalid weight sum in autorange calculation"); + return NAN; + } + + return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum; } } // namespace adc diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 3309bd04c5..01bbaeda15 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -10,13 +10,11 @@ from esphome.const import ( CONF_NUMBER, CONF_PIN, CONF_RAW, - CONF_WIFI, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_VOLT, ) from esphome.core import CORE -import esphome.final_validate as fv from . import ( ATTENUATION_MODES, @@ -24,6 +22,7 @@ from . import ( ESP32_VARIANT_ADC2_PIN_TO_CHANNEL, SAMPLING_MODES, adc_ns, + adc_unit_t, validate_adc_pin, ) @@ -57,21 +56,6 @@ def validate_config(config): return config -def final_validate_config(config): - if CORE.is_esp32: - variant = get_esp32_variant() - if ( - CONF_WIFI in fv.full_config.get() - and config[CONF_PIN][CONF_NUMBER] - in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant] - ): - raise cv.Invalid( - f"{variant} doesn't support ADC on this pin when Wi-Fi is configured" - ) - - return config - - ADCSensor = adc_ns.class_( "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler ) @@ -99,8 +83,6 @@ CONFIG_SCHEMA = cv.All( validate_config, ) -FINAL_VALIDATE_SCHEMA = final_validate_config - async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -119,13 +101,13 @@ async def to_code(config): cg.add(var.set_sample_count(config[CONF_SAMPLES])) cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE])) - if attenuation := config.get(CONF_ATTENUATION): - if attenuation == "auto": - cg.add(var.set_autorange(cg.global_ns.true)) - else: - cg.add(var.set_attenuation(attenuation)) - if CORE.is_esp32: + if attenuation := config.get(CONF_ATTENUATION): + if attenuation == "auto": + cg.add(var.set_autorange(cg.global_ns.true)) + else: + cg.add(var.set_attenuation(attenuation)) + variant = get_esp32_variant() pin_num = config[CONF_PIN][CONF_NUMBER] if ( @@ -133,10 +115,10 @@ async def to_code(config): and pin_num in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant] ): chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num] - cg.add(var.set_channel1(chan)) + cg.add(var.set_channel(adc_unit_t.ADC_UNIT_1, chan)) elif ( variant in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL and pin_num in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant] ): chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num] - cg.add(var.set_channel2(chan)) + cg.add(var.set_channel(adc_unit_t.ADC_UNIT_2, chan)) From 6486147da1fdf005f6155c219bf8dbc5dd918347 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 16 Jul 2025 11:05:27 +1000 Subject: [PATCH 90/90] [mipi_spi] Template code, partial buffer support (#9314) Co-authored-by: J. Nick Koston Co-authored-by: Keith Burzinski --- esphome/components/const/__init__.py | 1 + esphome/components/mipi_spi/__init__.py | 2 - esphome/components/mipi_spi/display.py | 376 +++++++--- esphome/components/mipi_spi/mipi_spi.cpp | 486 +------------ esphome/components/mipi_spi/mipi_spi.h | 668 +++++++++++++++--- .../components/mipi_spi/models/adafruit.py | 30 + esphome/components/mipi_spi/models/amoled.py | 10 +- .../components/mipi_spi/models/waveshare.py | 3 + esphome/components/spi/__init__.py | 34 +- tests/__init__.py | 1 + tests/component_tests/__init__.py | 0 .../component_tests/binary_sensor/__init__.py | 0 tests/component_tests/button/__init__.py | 0 tests/component_tests/conftest.py | 71 +- tests/component_tests/deep_sleep/__init__.py | 0 tests/component_tests/image/__init__.py | 0 tests/component_tests/mipi_spi/__init__.py | 0 .../mipi_spi/fixtures/lvgl.yaml | 25 + .../mipi_spi/fixtures/native.yaml | 20 + tests/component_tests/mipi_spi/test_init.py | 387 ++++++++++ tests/component_tests/ota/__init__.py | 0 tests/component_tests/packages/__init__.py | 0 tests/component_tests/sensor/__init__.py | 0 tests/component_tests/text/__init__.py | 0 tests/component_tests/text_sensor/__init__.py | 0 tests/component_tests/types.py | 21 + tests/component_tests/web_server/__init__.py | 0 .../test-esp32-2432s028.esp32-s3-idf.yaml | 41 -- .../test-jc3248w535.esp32-s3-idf.yaml | 41 -- .../test-jc3636w518.esp32-s3-idf.yaml | 19 - .../mipi_spi/test-lvgl.esp32-s3-idf.yaml | 18 + ...est-pico-restouch-lcd-35.esp32-s3-idf.yaml | 9 - .../mipi_spi/test-s3box.esp32-s3-idf.yaml | 41 -- .../mipi_spi/test-s3boxlite.esp32-s3-idf.yaml | 41 -- ...t-display-s3-amoled-plus.esp32-s3-idf.yaml | 9 - ...test-t-display-s3-amoled.esp32-s3-idf.yaml | 15 - .../test-t-display-s3-pro.esp32-s3-idf.yaml | 9 - .../test-t-display-s3.esp32-s3-idf.yaml | 37 - .../mipi_spi/test-t-display.esp32-s3-idf.yaml | 41 -- .../mipi_spi/test-t-embed.esp32-s3-idf.yaml | 9 - .../mipi_spi/test-t4-s3.esp32-s3-idf.yaml | 41 -- .../test-wt32-sc01-plus.esp32-s3-idf.yaml | 37 - 42 files changed, 1430 insertions(+), 1113 deletions(-) create mode 100644 esphome/components/mipi_spi/models/adafruit.py create mode 100644 tests/__init__.py create mode 100644 tests/component_tests/__init__.py create mode 100644 tests/component_tests/binary_sensor/__init__.py create mode 100644 tests/component_tests/button/__init__.py create mode 100644 tests/component_tests/deep_sleep/__init__.py create mode 100644 tests/component_tests/image/__init__.py create mode 100644 tests/component_tests/mipi_spi/__init__.py create mode 100644 tests/component_tests/mipi_spi/fixtures/lvgl.yaml create mode 100644 tests/component_tests/mipi_spi/fixtures/native.yaml create mode 100644 tests/component_tests/mipi_spi/test_init.py create mode 100644 tests/component_tests/ota/__init__.py create mode 100644 tests/component_tests/packages/__init__.py create mode 100644 tests/component_tests/sensor/__init__.py create mode 100644 tests/component_tests/text/__init__.py create mode 100644 tests/component_tests/text_sensor/__init__.py create mode 100644 tests/component_tests/types.py create mode 100644 tests/component_tests/web_server/__init__.py delete mode 100644 tests/components/mipi_spi/test-esp32-2432s028.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml create mode 100644 tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-pico-restouch-lcd-35.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-display-s3-amoled-plus.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-display-s3-amoled.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-display-s3-pro.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-display-s3.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml delete mode 100644 tests/components/mipi_spi/test-wt32-sc01-plus.esp32-s3-idf.yaml diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index b084622f4c..5b40545d89 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -3,6 +3,7 @@ CODEOWNERS = ["@esphome/core"] CONF_BYTE_ORDER = "byte_order" +CONF_COLOR_DEPTH = "color_depth" CONF_DRAW_ROUNDING = "draw_rounding" CONF_ON_STATE_CHANGE = "on_state_change" CONF_REQUEST_HEADERS = "request_headers" diff --git a/esphome/components/mipi_spi/__init__.py b/esphome/components/mipi_spi/__init__.py index 46b0206a1f..879efda619 100644 --- a/esphome/components/mipi_spi/__init__.py +++ b/esphome/components/mipi_spi/__init__.py @@ -2,10 +2,8 @@ CODEOWNERS = ["@clydebarrow"] DOMAIN = "mipi_spi" -CONF_DRAW_FROM_ORIGIN = "draw_from_origin" CONF_SPI_16 = "spi_16" CONF_PIXEL_MODE = "pixel_mode" -CONF_COLOR_DEPTH = "color_depth" CONF_BUS_MODE = "bus_mode" CONF_USE_AXIS_FLIPS = "use_axis_flips" CONF_NATIVE_WIDTH = "native_width" diff --git a/esphome/components/mipi_spi/display.py b/esphome/components/mipi_spi/display.py index 061257e859..d25dfd8539 100644 --- a/esphome/components/mipi_spi/display.py +++ b/esphome/components/mipi_spi/display.py @@ -3,11 +3,18 @@ import logging from esphome import pins import esphome.codegen as cg from esphome.components import display, spi +from esphome.components.const import ( + CONF_BYTE_ORDER, + CONF_COLOR_DEPTH, + CONF_DRAW_ROUNDING, +) +from esphome.components.display import CONF_SHOW_TEST_CARD, DISPLAY_ROTATIONS from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE import esphome.config_validation as cv from esphome.config_validation import ALLOW_EXTRA from esphome.const import ( CONF_BRIGHTNESS, + CONF_BUFFER_SIZE, CONF_COLOR_ORDER, CONF_CS_PIN, CONF_DATA_RATE, @@ -24,19 +31,19 @@ from esphome.const import ( 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 TimePeriod +from esphome.core import CORE, TimePeriod +from esphome.cpp_generator import TemplateArguments +from esphome.final_validate import full_config -from ..const import CONF_DRAW_ROUNDING -from ..lvgl.defines import CONF_COLOR_DEPTH from . import ( CONF_BUS_MODE, - CONF_DRAW_FROM_ORIGIN, CONF_NATIVE_HEIGHT, CONF_NATIVE_WIDTH, CONF_PIXEL_MODE, @@ -55,6 +62,7 @@ from .models import ( MADCTL_XFLIP, MADCTL_YFLIP, DriverChip, + adafruit, amoled, cyd, ili, @@ -69,43 +77,112 @@ DEPENDENCIES = ["spi"] LOGGER = logging.getLogger(DOMAIN) mipi_spi_ns = cg.esphome_ns.namespace("mipi_spi") -MipiSpi = mipi_spi_ns.class_( - "MipiSpi", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice +MipiSpi = mipi_spi_ns.class_("MipiSpi", display.Display, cg.Component, spi.SPIDevice) +MipiSpiBuffer = mipi_spi_ns.class_( + "MipiSpiBuffer", MipiSpi, display.Display, cg.Component, spi.SPIDevice ) ColorOrder = display.display_ns.enum("ColorMode") ColorBitness = display.display_ns.enum("ColorBitness") Model = mipi_spi_ns.enum("Model") +PixelMode = mipi_spi_ns.enum("PixelMode") +BusType = mipi_spi_ns.enum("BusType") + COLOR_ORDERS = { MODE_RGB: ColorOrder.COLOR_ORDER_RGB, MODE_BGR: ColorOrder.COLOR_ORDER_BGR, } COLOR_DEPTHS = { - 8: ColorBitness.COLOR_BITNESS_332, - 16: ColorBitness.COLOR_BITNESS_565, + 8: PixelMode.PIXEL_MODE_8, + 16: PixelMode.PIXEL_MODE_16, + 18: PixelMode.PIXEL_MODE_18, } + DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema +BusTypes = { + TYPE_SINGLE: BusType.BUS_TYPE_SINGLE, + TYPE_QUAD: BusType.BUS_TYPE_QUAD, + TYPE_OCTAL: BusType.BUS_TYPE_OCTAL, +} -DriverChip("CUSTOM", initsequence={}) +DriverChip("CUSTOM") MODELS = DriverChip.models -# These statements are noops, but serve to suppress linting of side-effect-only imports -for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare): +# This loop is a noop, but suppresses linting of side-effect-only imports +for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare, adafruit): pass -PixelMode = mipi_spi_ns.enum("PixelMode") -PIXEL_MODE_18BIT = "18bit" -PIXEL_MODE_16BIT = "16bit" +DISPLAY_18BIT = "18bit" +DISPLAY_16BIT = "16bit" -PIXEL_MODES = { - PIXEL_MODE_16BIT: 0x55, - PIXEL_MODE_18BIT: 0x66, +DISPLAY_PIXEL_MODES = { + DISPLAY_16BIT: (0x55, PixelMode.PIXEL_MODE_16), + DISPLAY_18BIT: (0x66, PixelMode.PIXEL_MODE_18), } +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. + The denominator must be a number between 2 and 16 that divides the display height evenly, + and the fraction represented by the denominator must be less than or equal to the given fraction. + :config: The configuration dictionary containing the buffer size fraction and display dimensions + :return: The denominator to use for the buffer size fraction + """ + 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) + try: + return next(x for x in range(2, 17) if frac >= 1 / x and height % x == 0) + except StopIteration: + raise cv.Invalid( + f"Buffer size fraction {frac} is not compatible with display height {height}" + ) from StopIteration + + def validate_dimension(rounding): def validator(value): value = cv.positive_int(value) @@ -158,41 +235,50 @@ def dimension_schema(rounding): ) -def model_schema(bus_mode, model: DriverChip, swapsies: bool): +def swap_xy_schema(model): + uses_swap = model.get_default(CONF_SWAP_XY, None) != cv.UNDEFINED + + def validator(value): + if value: + raise cv.Invalid("Axis swapping not supported by this model") + return cv.boolean(value) + + if uses_swap: + return {cv.Required(CONF_SWAP_XY): cv.boolean} + return {cv.Optional(CONF_SWAP_XY, default=False): validator} + + +def model_schema(config): + model = MODELS[config[CONF_MODEL]] + bus_mode = config.get(CONF_BUS_MODE, model.modes[0]) transform = cv.Schema( { cv.Required(CONF_MIRROR_X): cv.boolean, cv.Required(CONF_MIRROR_Y): cv.boolean, + **swap_xy_schema(model), } ) - if model.get_default(CONF_SWAP_XY, False) == cv.UNDEFINED: - transform = transform.extend( - { - cv.Optional(CONF_SWAP_XY): cv.invalid( - "Axis swapping not supported by this model" - ) - } - ) - else: - transform = transform.extend( - { - cv.Required(CONF_SWAP_XY): cv.boolean, - } - ) # CUSTOM model will need to provide a custom init sequence iseqconf = ( cv.Required(CONF_INIT_SEQUENCE) if model.initsequence is None else cv.Optional(CONF_INIT_SEQUENCE) ) - # Dimensions are optional if the model has a default width and the transform is not overridden + # Dimensions are optional if the model has a default width and the x-y transform is not overridden + is_swapped = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True cv_dimensions = ( - cv.Optional if model.get_default(CONF_WIDTH) and not swapsies else cv.Required + cv.Optional if model.get_default(CONF_WIDTH) and not is_swapped else cv.Required ) - pixel_modes = PIXEL_MODES if bus_mode == TYPE_SINGLE else (PIXEL_MODE_16BIT,) + pixel_modes = DISPLAY_PIXEL_MODES if bus_mode == TYPE_SINGLE else (DISPLAY_16BIT,) color_depth = ( ("16", "8", "16bit", "8bit") if bus_mode == TYPE_SINGLE else ("16", "16bit") ) + other_options = [ + CONF_INVERT_COLORS, + CONF_USE_AXIS_FLIPS, + ] + if bus_mode == TYPE_SINGLE: + other_options.append(CONF_SPI_16) schema = ( display.FULL_DISPLAY_SCHEMA.extend( spi.spi_device_schema( @@ -220,11 +306,13 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool): model.option(CONF_COLOR_ORDER, MODE_BGR): cv.enum( COLOR_ORDERS, upper=True ), + model.option(CONF_BYTE_ORDER, "big_endian"): cv.one_of( + "big_endian", "little_endian", lower=True + ), model.option(CONF_COLOR_DEPTH, 16): cv.one_of(*color_depth, lower=True), model.option(CONF_DRAW_ROUNDING, 2): power_of_two, - model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.Any( - cv.one_of(*pixel_modes, lower=True), - cv.int_range(0, 255, min_included=True, max_included=True), + model.option(CONF_PIXEL_MODE, DISPLAY_16BIT): cv.one_of( + *pixel_modes, lower=True ), cv.Optional(CONF_TRANSFORM): transform, cv.Optional(CONF_BUS_MODE, default=bus_mode): cv.one_of( @@ -232,19 +320,12 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool): ), cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True), iseqconf: cv.ensure_list(map_sequence), + cv.Optional(CONF_BUFFER_SIZE): cv.All( + cv.percentage, cv.Range(0.12, 1.0) + ), } ) - .extend( - { - model.option(x): cv.boolean - for x in [ - CONF_DRAW_FROM_ORIGIN, - CONF_SPI_16, - CONF_INVERT_COLORS, - CONF_USE_AXIS_FLIPS, - ] - } - ) + .extend({model.option(x): cv.boolean for x in other_options}) ) if brightness := model.get_default(CONF_BRIGHTNESS): schema = schema.extend( @@ -259,18 +340,25 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool): return schema -def rotation_as_transform(model, config): +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 config_schema(config): +def customise_schema(config): + """ + Create a customised config schema for a specific model and validate the configuration. + :param config: The configuration dictionary to validate + :return: The validated configuration dictionary + :raises cv.Invalid: If the configuration is invalid + """ # First get the model and bus mode config = cv.Schema( { @@ -288,29 +376,94 @@ def config_schema(config): extra=ALLOW_EXTRA, )(config) bus_mode = config.get(CONF_BUS_MODE, model.modes[0]) - swapsies = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True - config = model_schema(bus_mode, model, swapsies)(config) + config = model_schema(config)(config) # Check for invalid combinations of MADCTL config if init_sequence := config.get(CONF_INIT_SEQUENCE): - if MADCTL in [x[0] for x in init_sequence] and CONF_TRANSFORM in config: + commands = [x[0] for x in init_sequence] + if MADCTL in commands and CONF_TRANSFORM in config: raise cv.Invalid( f"transform is not supported when MADCTL ({MADCTL:#X}) is in the init sequence" ) + if PIXFMT in commands: + raise cv.Invalid( + f"PIXFMT ({PIXFMT:#X}) should not be in the init sequence, it will be set automatically" + ) if bus_mode == TYPE_QUAD and CONF_DC_PIN in config: raise cv.Invalid("DC pin is not supported in quad mode") - if config[CONF_PIXEL_MODE] == PIXEL_MODE_18BIT and bus_mode != TYPE_SINGLE: - raise cv.Invalid("18-bit pixel mode is not supported on a quad or octal bus") if bus_mode != TYPE_QUAD and CONF_DC_PIN not in config: raise cv.Invalid(f"DC pin is required in {bus_mode} mode") + denominator(config) return config -CONFIG_SCHEMA = config_schema +CONFIG_SCHEMA = customise_schema -def get_transform(model, config): - can_transform = rotation_as_transform(model, config) +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() + + from esphome.components.lvgl import DOMAIN as LVGL_DOMAIN + + if not requires_buffer(config) and LVGL_DOMAIN not in global_config: + # If no drawing methods are configured, and LVGL is not enabled, show a test card + config[CONF_SHOW_TEST_CARD] = True + + if "psram" not in global_config and CONF_BUFFER_SIZE not in config: + if not requires_buffer(config): + return config # No buffer needed, so no need to set a buffer size + # If PSRAM is not enabled, choose a small buffer size by default + if not requires_buffer(config): + # not our problem. + return config + color_depth = get_color_depth(config) + frac = denominator(config) + height, width, _offset_width, _offset_height = get_dimensions(config) + + buffer_size = color_depth // 8 * width * height // frac + # Target a buffer size of 20kB + fraction = 20000.0 / buffer_size + try: + config[CONF_BUFFER_SIZE] = 1.0 / next( + x for x in range(2, 17) if fraction >= 1 / x and height % x == 0 + ) + except StopIteration: + # Either the screen is too big, or the height is not divisible by any of the fractions, so use 1.0 + # PSRAM will be needed. + if CORE.is_esp32: + raise cv.Invalid( + "PSRAM is required for this display" + ) from StopIteration + + return config + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +def get_transform(config): + """ + Get the transformation configuration for the display. + :param config: + :return: + """ + model = MODELS[config[CONF_MODEL]] + can_transform = is_rotation_transformable(config) transform = config.get( CONF_TRANSFORM, { @@ -350,16 +503,13 @@ def get_sequence(model, config): 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 - if PIXFMT not in commands: - pixel_mode = config[CONF_PIXEL_MODE] - if not isinstance(pixel_mode, int): - pixel_mode = PIXEL_MODES[pixel_mode] - sequence.append((PIXFMT, pixel_mode)) + 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(model, config) + transform = get_transform(config) if transform.get(CONF_TRANSFORM): LOGGER.info("Using hardware transform to implement rotation") if transform.get(CONF_MIRROR_X): @@ -396,63 +546,62 @@ def get_sequence(model, config): ) +def get_instance(config): + """ + Get the type of MipiSpi instance to create based on the configuration, + and the template arguments. + :param config: + :return: type, template arguments + """ + width, height, offset_width, offset_height = get_dimensions(config) + + color_depth = int(config[CONF_COLOR_DEPTH].removesuffix("bit")) + bufferpixels = COLOR_DEPTHS[color_depth] + + display_pixel_mode = DISPLAY_PIXEL_MODES[config[CONF_PIXEL_MODE]][1] + bus_type = config[CONF_BUS_MODE] + if bus_type == TYPE_SINGLE and config.get(CONF_SPI_16, False): + # If the bus mode is single and spi_16 is set, use single 16-bit mode + bus_type = BusType.BUS_TYPE_SINGLE_16 + else: + bus_type = BusTypes[bus_type] + 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) + ] + templateargs = [ + buffer_type, + bufferpixels, + config[CONF_BYTE_ORDER] == "big_endian", + display_pixel_mode, + bus_type, + width, + height, + offset_width, + offset_height, + ] + # If a buffer is required, use MipiSpiBuffer, otherwise use MipiSpi + if requires_buffer(config): + templateargs.append(rotation) + templateargs.append(frac) + return MipiSpiBuffer, templateargs + return MipiSpi, templateargs + + async def to_code(config): model = MODELS[config[CONF_MODEL]] - transform = get_transform(model, 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] - else: - (width, height) = dimensions - offset_width = 0 - offset_height = 0 - else: - # Default dimensions, use model defaults and transform if needed - 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 - - color_depth = config[CONF_COLOR_DEPTH] - if color_depth.endswith("bit"): - color_depth = color_depth[:-3] - color_depth = COLOR_DEPTHS[int(color_depth)] - - var = cg.new_Pvariable( - config[CONF_ID], width, height, offset_width, offset_height, color_depth - ) + 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 rotation_as_transform(model, config): + if is_rotation_transformable(config): if CONF_TRANSFORM in config: LOGGER.warning("Use of 'transform' with 'rotation' is not recommended") else: config[CONF_ROTATION] = 0 cg.add(var.set_model(config[CONF_MODEL])) - cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN])) cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING])) - cg.add(var.set_spi_16(config[CONF_SPI_16])) if enable_pin := config.get(CONF_ENABLE_PIN): enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin] cg.add(var.set_enable_pins(enable)) @@ -472,4 +621,5 @@ async def to_code(config): cg.add(var.set_writer(lambda_)) await display.register_display(var, config) await spi.register_spi_device(var, config) + # Displays are write-only, set the SPI device to write-only as well cg.add(var.set_write_only(True)) diff --git a/esphome/components/mipi_spi/mipi_spi.cpp b/esphome/components/mipi_spi/mipi_spi.cpp index 962575477d..272915b4e1 100644 --- a/esphome/components/mipi_spi/mipi_spi.cpp +++ b/esphome/components/mipi_spi/mipi_spi.cpp @@ -2,489 +2,5 @@ #include "esphome/core/log.h" namespace esphome { -namespace mipi_spi { - -void MipiSpi::setup() { - ESP_LOGCONFIG(TAG, "Running setup"); - this->spi_setup(); - if (this->dc_pin_ != nullptr) { - this->dc_pin_->setup(); - this->dc_pin_->digital_write(false); - } - for (auto *pin : this->enable_pins_) { - pin->setup(); - pin->digital_write(true); - } - if (this->reset_pin_ != nullptr) { - this->reset_pin_->setup(); - this->reset_pin_->digital_write(true); - delay(5); - this->reset_pin_->digital_write(false); - delay(5); - this->reset_pin_->digital_write(true); - } - this->bus_width_ = this->parent_->get_bus_width(); - - // need to know when the display is ready for SLPOUT command - will be 120ms after reset - auto when = millis() + 120; - delay(10); - size_t index = 0; - auto &vec = this->init_sequence_; - while (index != vec.size()) { - if (vec.size() - index < 2) { - ESP_LOGE(TAG, "Malformed init sequence"); - this->mark_failed(); - return; - } - uint8_t cmd = vec[index++]; - uint8_t x = vec[index++]; - if (x == DELAY_FLAG) { - ESP_LOGD(TAG, "Delay %dms", cmd); - delay(cmd); - } else { - uint8_t num_args = x & 0x7F; - if (vec.size() - index < num_args) { - ESP_LOGE(TAG, "Malformed init sequence"); - this->mark_failed(); - return; - } - auto arg_byte = vec[index]; - switch (cmd) { - case SLEEP_OUT: { - // are we ready, boots? - int duration = when - millis(); - if (duration > 0) { - ESP_LOGD(TAG, "Sleep %dms", duration); - delay(duration); - } - } break; - - case INVERT_ON: - this->invert_colors_ = true; - break; - case MADCTL_CMD: - this->madctl_ = arg_byte; - break; - case PIXFMT: - this->pixel_mode_ = arg_byte & 0x11 ? PIXEL_MODE_16 : PIXEL_MODE_18; - break; - case BRIGHTNESS: - this->brightness_ = arg_byte; - break; - - default: - break; - } - const auto *ptr = vec.data() + index; - ESP_LOGD(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte); - this->write_command_(cmd, ptr, num_args); - index += num_args; - if (cmd == SLEEP_OUT) - delay(10); - } - } - this->setup_complete_ = true; - if (this->draw_from_origin_) - check_buffer_(); - ESP_LOGCONFIG(TAG, "MIPI SPI setup complete"); -} - -void MipiSpi::update() { - if (!this->setup_complete_ || this->is_failed()) { - return; - } - this->do_update_(); - if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) - return; - ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_); - // Some chips require that the drawing window be aligned on certain boundaries - auto dr = this->draw_rounding_; - this->x_low_ = this->x_low_ / dr * dr; - this->y_low_ = this->y_low_ / dr * dr; - this->x_high_ = (this->x_high_ + dr) / dr * dr - 1; - this->y_high_ = (this->y_high_ + dr) / dr * dr - 1; - if (this->draw_from_origin_) { - this->x_low_ = 0; - this->y_low_ = 0; - this->x_high_ = this->width_ - 1; - } - int w = this->x_high_ - this->x_low_ + 1; - int h = this->y_high_ - this->y_low_ + 1; - this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, this->y_low_, - this->width_ - w - this->x_low_); - // invalidate watermarks - this->x_low_ = this->width_; - this->y_low_ = this->height_; - this->x_high_ = 0; - this->y_high_ = 0; -} - -void MipiSpi::fill(Color color) { - if (!this->check_buffer_()) - return; - this->x_low_ = 0; - this->y_low_ = 0; - this->x_high_ = this->get_width_internal() - 1; - this->y_high_ = this->get_height_internal() - 1; - switch (this->color_depth_) { - case display::COLOR_BITNESS_332: { - auto new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); - memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_); - break; - } - default: { - auto new_color = display::ColorUtil::color_to_565(color); - if (((uint8_t) (new_color >> 8)) == ((uint8_t) new_color)) { - // Upper and lower is equal can use quicker memset operation. Takes ~20ms. - memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_); - } else { - auto *ptr_16 = reinterpret_cast(this->buffer_); - auto len = this->buffer_bytes_ / 2; - while (len--) { - *ptr_16++ = new_color; - } - } - } - } -} - -void MipiSpi::draw_absolute_pixel_internal(int x, int y, Color color) { - if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { - return; - } - if (!this->check_buffer_()) - return; - size_t pos = (y * this->width_) + x; - switch (this->color_depth_) { - case display::COLOR_BITNESS_332: { - uint8_t new_color = display::ColorUtil::color_to_332(color); - if (this->buffer_[pos] == new_color) - return; - this->buffer_[pos] = new_color; - break; - } - - case display::COLOR_BITNESS_565: { - auto *ptr_16 = reinterpret_cast(this->buffer_); - uint8_t hi_byte = static_cast(color.r & 0xF8) | (color.g >> 5); - uint8_t lo_byte = static_cast((color.g & 0x1C) << 3) | (color.b >> 3); - uint16_t new_color = hi_byte | (lo_byte << 8); // big endian - if (ptr_16[pos] == new_color) - return; - ptr_16[pos] = new_color; - break; - } - default: - return; - } - // low and high watermark may speed up drawing from buffer - if (x < this->x_low_) - this->x_low_ = x; - if (y < this->y_low_) - this->y_low_ = y; - if (x > this->x_high_) - this->x_high_ = x; - if (y > this->y_high_) - this->y_high_ = y; -} - -void MipiSpi::reset_params_() { - if (!this->is_ready()) - return; - this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); - if (this->brightness_.has_value()) - this->write_command_(BRIGHTNESS, this->brightness_.value()); -} - -void MipiSpi::write_init_sequence_() { - size_t index = 0; - auto &vec = this->init_sequence_; - while (index != vec.size()) { - if (vec.size() - index < 2) { - ESP_LOGE(TAG, "Malformed init sequence"); - this->mark_failed(); - return; - } - uint8_t cmd = vec[index++]; - uint8_t x = vec[index++]; - if (x == DELAY_FLAG) { - ESP_LOGV(TAG, "Delay %dms", cmd); - delay(cmd); - } else { - uint8_t num_args = x & 0x7F; - if (vec.size() - index < num_args) { - ESP_LOGE(TAG, "Malformed init sequence"); - this->mark_failed(); - return; - } - const auto *ptr = vec.data() + index; - this->write_command_(cmd, ptr, num_args); - index += num_args; - } - } - this->setup_complete_ = true; - ESP_LOGCONFIG(TAG, "MIPI SPI setup complete"); -} - -void MipiSpi::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { - ESP_LOGVV(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2); - uint8_t buf[4]; - x1 += this->offset_width_; - x2 += this->offset_width_; - y1 += this->offset_height_; - y2 += this->offset_height_; - put16_be(buf, y1); - put16_be(buf + 2, y2); - this->write_command_(RASET, buf, sizeof buf); - put16_be(buf, x1); - put16_be(buf + 2, x2); - this->write_command_(CASET, buf, sizeof buf); -} - -void MipiSpi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, - display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { - if (!this->setup_complete_ || this->is_failed()) - return; - if (w <= 0 || h <= 0) - return; - if (bitness != this->color_depth_ || big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) { - Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); - return; - } - if (this->draw_from_origin_) { - auto stride = x_offset + w + x_pad; - for (int y = 0; y != h; y++) { - memcpy(this->buffer_ + ((y + y_start) * this->width_ + x_start) * 2, - ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2); - } - ptr = this->buffer_; - w = this->width_; - h += y_start; - x_start = 0; - y_start = 0; - x_offset = 0; - y_offset = 0; - } - this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); -} - -void MipiSpi::write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride) { - stride -= w; - uint8_t transfer_buffer[6 * 256]; - size_t idx = 0; // index into transfer_buffer - while (h-- != 0) { - for (auto x = w; x-- != 0;) { - auto color_val = *ptr++; - // deal with byte swapping - transfer_buffer[idx++] = (color_val & 0xF8); // Blue - transfer_buffer[idx++] = ((color_val & 0x7) << 5) | ((color_val & 0xE000) >> 11); // Green - transfer_buffer[idx++] = (color_val >> 5) & 0xF8; // Red - if (idx == sizeof(transfer_buffer)) { - this->write_array(transfer_buffer, idx); - idx = 0; - } - } - ptr += stride; - } - if (idx != 0) - this->write_array(transfer_buffer, idx); -} - -void MipiSpi::write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) { - stride -= w; - uint8_t transfer_buffer[6 * 256]; - size_t idx = 0; // index into transfer_buffer - while (h-- != 0) { - for (auto x = w; x-- != 0;) { - auto color_val = *ptr++; - transfer_buffer[idx++] = color_val & 0xE0; // Red - transfer_buffer[idx++] = (color_val << 3) & 0xE0; // Green - transfer_buffer[idx++] = color_val << 6; // Blue - if (idx == sizeof(transfer_buffer)) { - this->write_array(transfer_buffer, idx); - idx = 0; - } - } - ptr += stride; - } - if (idx != 0) - this->write_array(transfer_buffer, idx); -} - -void MipiSpi::write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) { - stride -= w; - uint8_t transfer_buffer[6 * 256]; - size_t idx = 0; // index into transfer_buffer - while (h-- != 0) { - for (auto x = w; x-- != 0;) { - auto color_val = *ptr++; - transfer_buffer[idx++] = (color_val & 0xE0) | ((color_val & 0x1C) >> 2); - transfer_buffer[idx++] = (color_val & 0x3) << 3; - if (idx == sizeof(transfer_buffer)) { - this->write_array(transfer_buffer, idx); - idx = 0; - } - } - ptr += stride; - } - if (idx != 0) - this->write_array(transfer_buffer, idx); -} - -void MipiSpi::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, - int x_pad) { - this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); - auto stride = x_offset + w + x_pad; - const auto *offset_ptr = ptr; - if (this->color_depth_ == display::COLOR_BITNESS_332) { - offset_ptr += y_offset * stride + x_offset; - } else { - stride *= 2; - offset_ptr += y_offset * stride + x_offset * 2; - } - - switch (this->bus_width_) { - case 4: - this->enable(); - if (x_offset == 0 && x_pad == 0 && y_offset == 0) { - // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't - // bother - this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h * 2, 4); - } else { - this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, nullptr, 0, 4); - for (int y = 0; y != h; y++) { - this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 4); - offset_ptr += stride; - } - } - break; - - case 8: - this->write_command_(WDATA); - this->enable(); - if (x_offset == 0 && x_pad == 0 && y_offset == 0) { - this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h * 2, 8); - } else { - for (int y = 0; y != h; y++) { - this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 8); - offset_ptr += stride; - } - } - break; - - default: - this->write_command_(WDATA); - this->enable(); - - if (this->color_depth_ == display::COLOR_BITNESS_565) { - // Source buffer is 16-bit RGB565 - if (this->pixel_mode_ == PIXEL_MODE_18) { - // Convert RGB565 to RGB666 - this->write_18_from_16_bit_(reinterpret_cast(offset_ptr), w, h, stride / 2); - } else { - // Direct RGB565 output - if (x_offset == 0 && x_pad == 0 && y_offset == 0) { - this->write_array(ptr, w * h * 2); - } else { - for (int y = 0; y != h; y++) { - this->write_array(offset_ptr, w * 2); - offset_ptr += stride; - } - } - } - } else { - // Source buffer is 8-bit RGB332 - if (this->pixel_mode_ == PIXEL_MODE_18) { - // Convert RGB332 to RGB666 - this->write_18_from_8_bit_(offset_ptr, w, h, stride); - } else { - this->write_16_from_8_bit_(offset_ptr, w, h, stride); - } - break; - } - } - this->disable(); -} - -void MipiSpi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { - ESP_LOGV(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str()); - if (this->bus_width_ == 4) { - this->enable(); - this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len); - this->disable(); - } else if (this->bus_width_ == 8) { - this->dc_pin_->digital_write(false); - this->enable(); - this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8); - this->disable(); - this->dc_pin_->digital_write(true); - if (len != 0) { - this->enable(); - this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8); - this->disable(); - } - } else { - this->dc_pin_->digital_write(false); - this->enable(); - this->write_byte(cmd); - this->disable(); - this->dc_pin_->digital_write(true); - if (len != 0) { - if (this->spi_16_) { - for (size_t i = 0; i != len; i++) { - this->enable(); - this->write_byte(0); - this->write_byte(bytes[i]); - this->disable(); - } - } else { - this->enable(); - this->write_array(bytes, len); - this->disable(); - } - } - } -} - -void MipiSpi::dump_config() { - ESP_LOGCONFIG(TAG, - "MIPI_SPI Display\n" - " Model: %s\n" - " Width: %u\n" - " Height: %u", - this->model_, this->width_, this->height_); - if (this->offset_width_ != 0) - ESP_LOGCONFIG(TAG, " Offset width: %u", this->offset_width_); - if (this->offset_height_ != 0) - ESP_LOGCONFIG(TAG, " Offset height: %u", this->offset_height_); - ESP_LOGCONFIG(TAG, - " Swap X/Y: %s\n" - " Mirror X: %s\n" - " Mirror Y: %s\n" - " Color depth: %d bits\n" - " Invert colors: %s\n" - " Color order: %s\n" - " Pixel mode: %s", - YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)), - YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), - this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8, YESNO(this->invert_colors_), - this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit"); - if (this->brightness_.has_value()) - ESP_LOGCONFIG(TAG, " Brightness: %u", this->brightness_.value()); - if (this->spi_16_) - ESP_LOGCONFIG(TAG, " SPI 16bit: YES"); - ESP_LOGCONFIG(TAG, " Draw rounding: %u", this->draw_rounding_); - if (this->draw_from_origin_) - ESP_LOGCONFIG(TAG, " Draw from origin: YES"); - LOG_PIN(" CS Pin: ", this->cs_); - LOG_PIN(" Reset Pin: ", this->reset_pin_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - ESP_LOGCONFIG(TAG, - " SPI Mode: %d\n" - " SPI Data rate: %dMHz\n" - " SPI Bus width: %d", - this->mode_, static_cast(this->data_rate_ / 1000000), this->bus_width_); -} - -} // namespace mipi_spi +namespace mipi_spi {} // namespace mipi_spi } // namespace esphome diff --git a/esphome/components/mipi_spi/mipi_spi.h b/esphome/components/mipi_spi/mipi_spi.h index 052ebe3a6b..cdba5a3235 100644 --- a/esphome/components/mipi_spi/mipi_spi.h +++ b/esphome/components/mipi_spi/mipi_spi.h @@ -4,40 +4,39 @@ #include "esphome/components/spi/spi.h" #include "esphome/components/display/display.h" -#include "esphome/components/display/display_buffer.h" #include "esphome/components/display/display_color_utils.h" namespace esphome { namespace mipi_spi { constexpr static const char *const TAG = "display.mipi_spi"; -static const uint8_t SW_RESET_CMD = 0x01; -static const uint8_t SLEEP_OUT = 0x11; -static const uint8_t NORON = 0x13; -static const uint8_t INVERT_OFF = 0x20; -static const uint8_t INVERT_ON = 0x21; -static const uint8_t ALL_ON = 0x23; -static const uint8_t WRAM = 0x24; -static const uint8_t MIPI = 0x26; -static const uint8_t DISPLAY_ON = 0x29; -static const uint8_t RASET = 0x2B; -static const uint8_t CASET = 0x2A; -static const uint8_t WDATA = 0x2C; -static const uint8_t TEON = 0x35; -static const uint8_t MADCTL_CMD = 0x36; -static const uint8_t PIXFMT = 0x3A; -static const uint8_t BRIGHTNESS = 0x51; -static const uint8_t SWIRE1 = 0x5A; -static const uint8_t SWIRE2 = 0x5B; -static const uint8_t PAGESEL = 0xFE; +static constexpr uint8_t SW_RESET_CMD = 0x01; +static constexpr uint8_t SLEEP_OUT = 0x11; +static constexpr uint8_t NORON = 0x13; +static constexpr uint8_t INVERT_OFF = 0x20; +static constexpr uint8_t INVERT_ON = 0x21; +static constexpr uint8_t ALL_ON = 0x23; +static constexpr uint8_t WRAM = 0x24; +static constexpr uint8_t MIPI = 0x26; +static constexpr uint8_t DISPLAY_ON = 0x29; +static constexpr uint8_t RASET = 0x2B; +static constexpr uint8_t CASET = 0x2A; +static constexpr uint8_t WDATA = 0x2C; +static constexpr uint8_t TEON = 0x35; +static constexpr uint8_t MADCTL_CMD = 0x36; +static constexpr uint8_t PIXFMT = 0x3A; +static constexpr uint8_t BRIGHTNESS = 0x51; +static constexpr uint8_t SWIRE1 = 0x5A; +static constexpr uint8_t SWIRE2 = 0x5B; +static constexpr uint8_t PAGESEL = 0xFE; -static const uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top -static const uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left -static const uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes -static const uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order -static const uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order -static const uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally -static const uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically +static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top +static constexpr uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left +static constexpr uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes +static constexpr uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order +static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order +static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally +static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically static const uint8_t DELAY_FLAG = 0xFF; // store a 16 bit value in a buffer, big endian. @@ -46,28 +45,44 @@ static inline void put16_be(uint8_t *buf, uint16_t value) { buf[1] = value; } +// Buffer mode, conveniently also the number of bytes in a pixel enum PixelMode { - PIXEL_MODE_16, - PIXEL_MODE_18, + PIXEL_MODE_8 = 1, + PIXEL_MODE_16 = 2, + PIXEL_MODE_18 = 3, }; -class MipiSpi : public display::DisplayBuffer, +enum BusType { + BUS_TYPE_SINGLE = 1, + BUS_TYPE_QUAD = 4, + BUS_TYPE_OCTAL = 8, + BUS_TYPE_SINGLE_16 = 16, // Single bit bus, but 16 bits per transfer +}; + +/** + * Base class for MIPI SPI displays. + * All the methods are defined here in the header file, as it is not possible to define templated methods in a cpp file. + * + * @tparam BUFFERTYPE The type of the buffer pixels, e.g. uint8_t or uint16_t + * @tparam BUFFERPIXEL Color depth of the buffer + * @tparam DISPLAYPIXEL Color depth of the display + * @tparam BUS_TYPE The type of the interface bus (single, quad, octal) + * @tparam WIDTH Width of the display in pixels + * @tparam HEIGHT Height of the display in pixels + * @tparam OFFSET_WIDTH The x-offset of the display in pixels + * @tparam OFFSET_HEIGHT The y-offset of the display in pixels + * buffer + */ +template +class MipiSpi : public display::Display, public spi::SPIDevice { public: - MipiSpi(size_t width, size_t height, int16_t offset_width, int16_t offset_height, display::ColorBitness color_depth) - : width_(width), - height_(height), - offset_width_(offset_width), - offset_height_(offset_height), - color_depth_(color_depth) {} + MipiSpi() {} + void update() override { this->stop_poller(); } + void draw_pixel_at(int x, int y, Color color) override {} void set_model(const char *model) { this->model_ = model; } - void update() override; - void setup() override; - display::ColorOrder get_color_mode() { - return this->madctl_ & MADCTL_BGR ? display::COLOR_ORDER_BGR : display::COLOR_ORDER_RGB; - } - void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } void set_enable_pins(std::vector enable_pins) { this->enable_pins_ = std::move(enable_pins); } void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; } @@ -79,93 +94,524 @@ class MipiSpi : public display::DisplayBuffer, this->brightness_ = brightness; this->reset_params_(); } - - void set_draw_from_origin(bool draw_from_origin) { this->draw_from_origin_ = draw_from_origin; } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } - void dump_config() override; - int get_width_internal() override { return this->width_; } - int get_height_internal() override { return this->height_; } - bool can_proceed() override { return this->setup_complete_; } + int get_width_internal() override { return WIDTH; } + int get_height_internal() override { return HEIGHT; } void set_init_sequence(const std::vector &sequence) { this->init_sequence_ = sequence; } void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; } - void set_spi_16(bool spi_16) { this->spi_16_ = spi_16; } + + // reset the display, and write the init sequence + void setup() override { + this->spi_setup(); + if (this->dc_pin_ != nullptr) { + this->dc_pin_->setup(); + this->dc_pin_->digital_write(false); + } + for (auto *pin : this->enable_pins_) { + pin->setup(); + pin->digital_write(true); + } + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(5); + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + } + + // need to know when the display is ready for SLPOUT command - will be 120ms after reset + auto when = millis() + 120; + delay(10); + size_t index = 0; + auto &vec = this->init_sequence_; + while (index != vec.size()) { + if (vec.size() - index < 2) { + esph_log_e(TAG, "Malformed init sequence"); + this->mark_failed(); + return; + } + uint8_t cmd = vec[index++]; + uint8_t x = vec[index++]; + if (x == DELAY_FLAG) { + esph_log_d(TAG, "Delay %dms", cmd); + delay(cmd); + } else { + uint8_t num_args = x & 0x7F; + if (vec.size() - index < num_args) { + esph_log_e(TAG, "Malformed init sequence"); + this->mark_failed(); + return; + } + auto arg_byte = vec[index]; + switch (cmd) { + case SLEEP_OUT: { + // are we ready, boots? + int duration = when - millis(); + if (duration > 0) { + esph_log_d(TAG, "Sleep %dms", duration); + delay(duration); + } + } break; + + case INVERT_ON: + this->invert_colors_ = true; + break; + case MADCTL_CMD: + this->madctl_ = arg_byte; + break; + case BRIGHTNESS: + this->brightness_ = arg_byte; + break; + + default: + break; + } + const auto *ptr = vec.data() + index; + esph_log_d(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte); + this->write_command_(cmd, ptr, num_args); + index += num_args; + if (cmd == SLEEP_OUT) + delay(10); + } + } + // init sequence no longer needed + this->init_sequence_.clear(); + } + + // Drawing operations + + void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override { + if (this->is_failed()) + return; + if (w <= 0 || h <= 0) + return; + if (get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) { + // note that the usual logging macros are banned in header files, so use their replacement + esph_log_e(TAG, "Unsupported color depth or bit order"); + return; + } + this->write_to_display_(x_start, y_start, w, h, reinterpret_cast(ptr), x_offset, y_offset, + x_pad); + } + + void dump_config() override { + esph_log_config(TAG, + "MIPI_SPI Display\n" + " Model: %s\n" + " Width: %u\n" + " Height: %u", + this->model_, WIDTH, HEIGHT); + if constexpr (OFFSET_WIDTH != 0) + esph_log_config(TAG, " Offset width: %u", OFFSET_WIDTH); + if constexpr (OFFSET_HEIGHT != 0) + esph_log_config(TAG, " Offset height: %u", OFFSET_HEIGHT); + esph_log_config(TAG, + " Swap X/Y: %s\n" + " Mirror X: %s\n" + " Mirror Y: %s\n" + " Invert colors: %s\n" + " Color order: %s\n" + " Display pixels: %d bits\n" + " Endianness: %s\n", + YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)), + YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), YESNO(this->invert_colors_), + this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", DISPLAYPIXEL * 8, IS_BIG_ENDIAN ? "Big" : "Little"); + if (this->brightness_.has_value()) + esph_log_config(TAG, " Brightness: %u", this->brightness_.value()); + if (this->cs_ != nullptr) + esph_log_config(TAG, " CS Pin: %s", this->cs_->dump_summary().c_str()); + if (this->reset_pin_ != nullptr) + esph_log_config(TAG, " Reset Pin: %s", this->reset_pin_->dump_summary().c_str()); + if (this->dc_pin_ != nullptr) + esph_log_config(TAG, " DC Pin: %s", this->dc_pin_->dump_summary().c_str()); + esph_log_config(TAG, + " SPI Mode: %d\n" + " SPI Data rate: %dMHz\n" + " SPI Bus width: %d", + this->mode_, static_cast(this->data_rate_ / 1000000), BUS_TYPE); + } protected: - bool check_buffer_() { - if (this->is_failed()) - return false; - if (this->buffer_ != nullptr) - return true; - auto bytes_per_pixel = this->color_depth_ == display::COLOR_BITNESS_565 ? 2 : 1; - this->init_internal_(this->width_ * this->height_ * bytes_per_pixel); - if (this->buffer_ == nullptr) { - this->mark_failed(); - return false; - } - this->buffer_bytes_ = this->width_ * this->height_ * bytes_per_pixel; - return true; - } - void fill(Color color) override; - void draw_absolute_pixel_internal(int x, int y, Color color) override; - void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, - display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; - void write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride); - void write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride); - void write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride); - void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, - int x_pad); - /** - * the RM67162 in quad SPI mode seems to work like this (not in the datasheet, this is deduced from the - * sample code.) - * - * Immediately after enabling /CS send 4 bytes in single-dataline SPI mode: - * 0: either 0x2 or 0x32. The first indicates that any subsequent data bytes after the initial 4 will be - * sent in 1-dataline SPI. The second indicates quad mode. - * 1: 0x00 - * 2: The command (register address) byte. - * 3: 0x00 - * - * This is followed by zero or more data bytes in either 1-wire or 4-wire mode, depending on the first byte. - * At the conclusion of the write, de-assert /CS. - * - * @param cmd - * @param bytes - * @param len - */ - void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len); - + /* METHODS */ + // convenience functions to write commands with or without data void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); } void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); } - void reset_params_(); - void write_init_sequence_(); - void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); + // Writes a command to the display, with the given bytes. + void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { + esph_log_v(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str()); + if constexpr (BUS_TYPE == BUS_TYPE_QUAD) { + this->enable(); + this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len); + this->disable(); + } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) { + this->dc_pin_->digital_write(false); + this->enable(); + this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8); + this->disable(); + this->dc_pin_->digital_write(true); + if (len != 0) { + this->enable(); + this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8); + this->disable(); + } + } else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE) { + this->dc_pin_->digital_write(false); + this->enable(); + this->write_byte(cmd); + this->disable(); + this->dc_pin_->digital_write(true); + if (len != 0) { + this->enable(); + this->write_array(bytes, len); + this->disable(); + } + } else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE_16) { + this->dc_pin_->digital_write(false); + this->enable(); + this->write_byte(cmd); + this->disable(); + this->dc_pin_->digital_write(true); + for (size_t i = 0; i != len; i++) { + this->enable(); + this->write_byte(0); + this->write_byte(bytes[i]); + this->disable(); + } + } + } + + // write changed parameters to the display + void reset_params_() { + if (!this->is_ready()) + return; + this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); + if (this->brightness_.has_value()) + this->write_command_(BRIGHTNESS, this->brightness_.value()); + } + + // set the address window for the next data write + void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { + esph_log_v(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2); + uint8_t buf[4]; + x1 += OFFSET_WIDTH; + x2 += OFFSET_WIDTH; + y1 += OFFSET_HEIGHT; + y2 += OFFSET_HEIGHT; + put16_be(buf, y1); + put16_be(buf + 2, y2); + this->write_command_(RASET, buf, sizeof buf); + put16_be(buf, x1); + put16_be(buf + 2, x2); + this->write_command_(CASET, buf, sizeof buf); + if constexpr (BUS_TYPE != BUS_TYPE_QUAD) { + this->write_command_(WDATA); + } + } + + // map the display color bitness to the pixel mode + static PixelMode get_pixel_mode(display::ColorBitness bitness) { + switch (bitness) { + case display::COLOR_BITNESS_888: + return PIXEL_MODE_18; // 18 bits per pixel + case display::COLOR_BITNESS_565: + return PIXEL_MODE_16; // 16 bits per pixel + default: + return PIXEL_MODE_8; // Default to 8 bits per pixel + } + } + + /** + * Writes a buffer to the display. + * @param w Width of each line in bytes + * @param h Height of the buffer in rows + * @param pad Padding in bytes after each line + */ + void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad) { + if (pad == 0) { + if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) { + this->write_array(ptr, w * h); + } else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) { + this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h, 4); + } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) { + this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h, 8); + } + } else { + for (size_t y = 0; y != h; y++) { + if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) { + this->write_array(ptr, w); + } else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) { + this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w, 4); + } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) { + this->write_cmd_addr_data(0, 0, 0, 0, ptr, w, 8); + } + ptr += w + pad; + } + } + } + + /** + * Writes a buffer to the display. + * + * The ptr is a pointer to the pixel data + * The other parameters are all in pixel units. + */ + void write_to_display_(int x_start, int y_start, int w, int h, const BUFFERTYPE *ptr, int x_offset, int y_offset, + int x_pad) { + this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); + this->enable(); + ptr += y_offset * (x_offset + w + x_pad) + x_offset; + if constexpr (BUFFERPIXEL == DISPLAYPIXEL) { + this->write_display_data_(reinterpret_cast(ptr), w * sizeof(BUFFERTYPE), h, + x_pad * sizeof(BUFFERTYPE)); + } else { + // type conversion required, do it in chunks + uint8_t dbuffer[DISPLAYPIXEL * 48]; + uint8_t *dptr = dbuffer; + auto stride = x_offset + w + x_pad; // stride in pixels + for (size_t y = 0; y != h; y++) { + for (size_t x = 0; x != w; x++) { + auto color_val = ptr[y * stride + x]; + if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_16) { + // 16 to 18 bit conversion + if constexpr (IS_BIG_ENDIAN) { + *dptr++ = color_val & 0xF8; + *dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11; + *dptr++ = (color_val >> 5) & 0xF8; + } else { + *dptr++ = (color_val >> 8) & 0xF8; // Blue + *dptr++ = (color_val & 0x7E0) >> 3; + *dptr++ = color_val << 3; + } + } else if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_8) { + // 8 bit to 18 bit conversion + *dptr++ = color_val << 6; // Blue + *dptr++ = (color_val & 0x1C) << 3; // Green + *dptr++ = (color_val & 0xE0); // Red + } else if constexpr (DISPLAYPIXEL == PIXEL_MODE_16 && BUFFERPIXEL == PIXEL_MODE_8) { + if constexpr (IS_BIG_ENDIAN) { + *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2); + *dptr++ = (color_val & 3) << 3; + } else { + *dptr++ = (color_val & 3) << 3; + *dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2); + } + } + // buffer full? Flush. + if (dptr == dbuffer + sizeof(dbuffer)) { + this->write_display_data_(dbuffer, sizeof(dbuffer), 1, 0); + dptr = dbuffer; + } + } + } + // flush any remaining data + if (dptr != dbuffer) { + this->write_display_data_(dbuffer, dptr - dbuffer, 1, 0); + } + } + this->disable(); + } + + /* PROPERTIES */ + + // GPIO pins GPIOPin *reset_pin_{nullptr}; std::vector enable_pins_{}; GPIOPin *dc_pin_{nullptr}; - uint16_t x_low_{1}; - uint16_t y_low_{1}; - uint16_t x_high_{0}; - uint16_t y_high_{0}; - bool setup_complete_{}; + // other properties set by configuration bool invert_colors_{}; - size_t width_; - size_t height_; - int16_t offset_width_; - int16_t offset_height_; - size_t buffer_bytes_{0}; - display::ColorBitness color_depth_; - PixelMode pixel_mode_{PIXEL_MODE_16}; - uint8_t bus_width_{}; - bool spi_16_{}; - uint8_t madctl_{}; - bool draw_from_origin_{false}; unsigned draw_rounding_{2}; optional brightness_{}; const char *model_{"Unknown"}; std::vector init_sequence_{}; + uint8_t madctl_{}; }; + +/** + * Class for MIPI SPI displays with a buffer. + * + * @tparam BUFFERTYPE The type of the buffer pixels, e.g. uint8_t or uint16_t + * @tparam BUFFERPIXEL Color depth of the buffer + * @tparam DISPLAYPIXEL Color depth of the display + * @tparam BUS_TYPE The type of the interface bus (single, quad, octal) + * @tparam ROTATION The rotation of the display + * @tparam WIDTH Width of the display in pixels + * @tparam HEIGHT Height of the display in pixels + * @tparam OFFSET_WIDTH The x-offset of the display in pixels + * @tparam OFFSET_HEIGHT The y-offset of the display in pixels + * @tparam FRACTION The fraction of the display size to use for the buffer (e.g. 4 means a 1/4 buffer). + */ +template +class MipiSpiBuffer : public MipiSpi { + public: + MipiSpiBuffer() { this->rotation_ = ROTATION; } + + void dump_config() override { + MipiSpi::dump_config(); + esph_log_config(TAG, + " Rotation: %d°\n" + " Buffer pixels: %d bits\n" + " Buffer fraction: 1/%d\n" + " Buffer bytes: %zu\n" + " Draw rounding: %u", + this->rotation_, BUFFERPIXEL * 8, FRACTION, sizeof(BUFFERTYPE) * WIDTH * HEIGHT / FRACTION, + this->draw_rounding_); + } + + void setup() override { + MipiSpi::setup(); + RAMAllocator allocator{}; + this->buffer_ = allocator.allocate(WIDTH * HEIGHT / FRACTION); + if (this->buffer_ == nullptr) { + this->mark_failed("Buffer allocation failed"); + } + } + + void update() override { +#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE + auto now = millis(); +#endif + if (this->is_failed()) { + return; + } + // for updates with a small buffer, we repeatedly call the writer_ function, clipping the height to a fraction of + // the display height, + for (this->start_line_ = 0; this->start_line_ < HEIGHT; this->start_line_ += HEIGHT / FRACTION) { +#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE + auto lap = millis(); +#endif + this->end_line_ = this->start_line_ + HEIGHT / FRACTION; + if (this->auto_clear_enabled_) { + this->clear(); + } + if (this->page_ != nullptr) { + this->page_->get_writer()(*this); + } else if (this->writer_.has_value()) { + (*this->writer_)(*this); + } else { + this->test_card(); + } +#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE + esph_log_v(TAG, "Drawing from line %d took %dms", this->start_line_, millis() - lap); + lap = millis(); +#endif + if (this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) + return; + esph_log_v(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, + this->y_high_); + // Some chips require that the drawing window be aligned on certain boundaries + auto dr = this->draw_rounding_; + this->x_low_ = this->x_low_ / dr * dr; + this->y_low_ = this->y_low_ / dr * dr; + this->x_high_ = (this->x_high_ + dr) / dr * dr - 1; + this->y_high_ = (this->y_high_ + dr) / dr * dr - 1; + int w = this->x_high_ - this->x_low_ + 1; + int h = this->y_high_ - this->y_low_ + 1; + this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, + this->y_low_ - this->start_line_, WIDTH - w); + // invalidate watermarks + this->x_low_ = WIDTH; + this->y_low_ = HEIGHT; + this->x_high_ = 0; + this->y_high_ = 0; +#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE + esph_log_v(TAG, "Write to display took %dms", millis() - lap); + lap = millis(); +#endif + } +#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE + esph_log_v(TAG, "Total update took %dms", millis() - now); +#endif + } + + // Draw a pixel at the given coordinates. + void draw_pixel_at(int x, int y, Color color) override { + rotate_coordinates_(x, y); + if (x < 0 || x >= WIDTH || y < this->start_line_ || y >= this->end_line_) + return; + this->buffer_[(y - this->start_line_) * WIDTH + x] = convert_color_(color); + if (x < this->x_low_) { + this->x_low_ = x; + } + if (x > this->x_high_) { + this->x_high_ = x; + } + if (y < this->y_low_) { + this->y_low_ = y; + } + if (y > this->y_high_) { + this->y_high_ = y; + } + } + + // Fills the display with a color. + void fill(Color color) override { + this->x_low_ = 0; + this->y_low_ = this->start_line_; + this->x_high_ = WIDTH - 1; + this->y_high_ = this->end_line_ - 1; + std::fill_n(this->buffer_, HEIGHT * WIDTH / FRACTION, convert_color_(color)); + } + + int get_width() override { + if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES || ROTATION == display::DISPLAY_ROTATION_270_DEGREES) + return HEIGHT; + return WIDTH; + } + + int get_height() override { + if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES || ROTATION == display::DISPLAY_ROTATION_270_DEGREES) + return WIDTH; + return HEIGHT; + } + + protected: + // Rotate the coordinates to match the display orientation. + void rotate_coordinates_(int &x, int &y) const { + if constexpr (ROTATION == display::DISPLAY_ROTATION_180_DEGREES) { + x = WIDTH - x - 1; + y = HEIGHT - y - 1; + } else if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES) { + auto tmp = x; + x = WIDTH - y - 1; + y = tmp; + } else if constexpr (ROTATION == display::DISPLAY_ROTATION_270_DEGREES) { + auto tmp = y; + y = HEIGHT - x - 1; + x = tmp; + } + } + + // Convert a color to the buffer pixel format. + BUFFERTYPE convert_color_(Color &color) const { + if constexpr (BUFFERPIXEL == PIXEL_MODE_8) { + return (color.red & 0xE0) | (color.g & 0xE0) >> 3 | color.b >> 6; + } else if constexpr (BUFFERPIXEL == PIXEL_MODE_16) { + if constexpr (IS_BIG_ENDIAN) { + return (color.r & 0xF8) | color.g >> 5 | (color.g & 0x1C) << 11 | (color.b & 0xF8) << 5; + } else { + return (color.r & 0xF8) << 8 | (color.g & 0xFC) << 3 | color.b >> 3; + } + } + return static_cast(0); + } + + BUFFERTYPE *buffer_{}; + uint16_t x_low_{WIDTH}; + uint16_t y_low_{HEIGHT}; + uint16_t x_high_{0}; + uint16_t y_high_{0}; + uint16_t start_line_{0}; + uint16_t end_line_{1}; +}; + } // namespace mipi_spi } // namespace esphome diff --git a/esphome/components/mipi_spi/models/adafruit.py b/esphome/components/mipi_spi/models/adafruit.py new file mode 100644 index 0000000000..0e91107bee --- /dev/null +++ b/esphome/components/mipi_spi/models/adafruit.py @@ -0,0 +1,30 @@ +from .ili import ST7789V + +ST7789V.extend( + "ADAFRUIT-FUNHOUSE", + height=240, + width=240, + offset_height=0, + offset_width=0, + cs_pin=40, + dc_pin=39, + reset_pin=41, + invert_colors=True, + mirror_x=True, + mirror_y=True, + data_rate="80MHz", +) + +ST7789V.extend( + "ADAFRUIT-S2-TFT-FEATHER", + height=240, + width=135, + offset_height=52, + offset_width=40, + cs_pin=7, + dc_pin=39, + reset_pin=40, + invert_colors=True, +) + +models = {} diff --git a/esphome/components/mipi_spi/models/amoled.py b/esphome/components/mipi_spi/models/amoled.py index 14277b243f..882d19db30 100644 --- a/esphome/components/mipi_spi/models/amoled.py +++ b/esphome/components/mipi_spi/models/amoled.py @@ -67,6 +67,14 @@ RM690B0 = DriverChip( ), ) -T4_S3_AMOLED = RM690B0.extend("T4-S3", width=450, offset_width=16, bus_mode=TYPE_QUAD) +T4_S3_AMOLED = RM690B0.extend( + "T4-S3", + width=450, + offset_width=16, + cs_pin=11, + reset_pin=13, + enable_pin=9, + bus_mode=TYPE_QUAD, +) models = {} diff --git a/esphome/components/mipi_spi/models/waveshare.py b/esphome/components/mipi_spi/models/waveshare.py index 6d14f56fc6..726718aaf6 100644 --- a/esphome/components/mipi_spi/models/waveshare.py +++ b/esphome/components/mipi_spi/models/waveshare.py @@ -1,3 +1,5 @@ +import esphome.config_validation as cv + from . import DriverChip from .ili import ILI9488_A @@ -128,6 +130,7 @@ DriverChip( ILI9488_A.extend( "PICO-RESTOUCH-LCD-3.5", + swap_xy=cv.UNDEFINED, spi_16=True, pixel_mode="16bit", mirror_x=True, diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 58bfc3f411..065ccc2668 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -1,4 +1,5 @@ import re +from typing import Any from esphome import pins import esphome.codegen as cg @@ -139,6 +140,27 @@ def get_hw_interface_list(): return [] +def one_of_interface_validator(additional_values: list[str] | None = None) -> Any: + """Helper to create a one_of validator for SPI interfaces. + + This delays evaluation of get_hw_interface_list() until validation time, + avoiding access to CORE.data during module import. + + Args: + additional_values: List of additional valid values to include + """ + if additional_values is None: + additional_values = [] + + def validator(value: str) -> str: + return cv.one_of( + *sum(get_hw_interface_list(), additional_values), + lower=True, + )(value) + + return cv.All(cv.string, validator) + + # Given an SPI name, return the index of it in the available list def get_spi_index(name): for i, ilist in enumerate(get_hw_interface_list()): @@ -274,9 +296,8 @@ SPI_SINGLE_SCHEMA = cv.All( cv.Optional(CONF_FORCE_SW): cv.invalid( "force_sw is deprecated - use interface: software" ), - cv.Optional(CONF_INTERFACE, default="any"): cv.one_of( - *sum(get_hw_interface_list(), ["software", "hardware", "any"]), - lower=True, + cv.Optional(CONF_INTERFACE, default="any"): one_of_interface_validator( + ["software", "hardware", "any"] ), cv.Optional(CONF_DATA_PINS): cv.invalid( "'data_pins' should be used with 'type: quad or octal' only" @@ -309,10 +330,9 @@ def spi_mode_schema(mode): cv.ensure_list(pins.internal_gpio_output_pin_number), cv.Length(min=pin_count, max=pin_count), ), - cv.Optional(CONF_INTERFACE, default="hardware"): cv.one_of( - *sum(get_hw_interface_list(), ["hardware"]), - lower=True, - ), + cv.Optional( + CONF_INTERFACE, default="hardware" + ): one_of_interface_validator(["hardware"]), cv.Optional(CONF_MISO_PIN): cv.invalid( f"'miso_pin' should not be used with {mode} SPI" ), diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000..d49aac4bab --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""ESPHome tests package.""" diff --git a/tests/component_tests/__init__.py b/tests/component_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/binary_sensor/__init__.py b/tests/component_tests/binary_sensor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/button/__init__.py b/tests/component_tests/button/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/conftest.py b/tests/component_tests/conftest.py index b1e0eaa200..b269e23cd6 100644 --- a/tests/component_tests/conftest.py +++ b/tests/component_tests/conftest.py @@ -5,18 +5,30 @@ from __future__ import annotations from collections.abc import Callable, Generator from pathlib import Path import sys +from typing import Any import pytest +from esphome import config, final_validate +from esphome.const import ( + KEY_CORE, + KEY_TARGET_FRAMEWORK, + KEY_TARGET_PLATFORM, + PlatformFramework, +) +from esphome.types import ConfigType + # Add package root to python path here = Path(__file__).parent package_root = here.parent.parent sys.path.insert(0, package_root.as_posix()) from esphome.__main__ import generate_cpp_contents # noqa: E402 -from esphome.config import read_config # noqa: E402 +from esphome.config import Config, read_config # noqa: E402 from esphome.core import CORE # noqa: E402 +from .types import SetCoreConfigCallable # noqa: E402 + @pytest.fixture(autouse=True) def config_path(request: pytest.FixtureRequest) -> Generator[None]: @@ -36,6 +48,59 @@ def config_path(request: pytest.FixtureRequest) -> Generator[None]: CORE.config_path = original_path +@pytest.fixture(autouse=True) +def reset_core() -> Generator[None]: + """Reset CORE after each test.""" + yield + CORE.reset() + + +@pytest.fixture +def set_core_config() -> Generator[SetCoreConfigCallable]: + """Fixture to set up the core configuration for tests.""" + + def setter( + platform_framework: PlatformFramework, + /, + *, + core_data: ConfigType | None = None, + platform_data: ConfigType | None = None, + ) -> None: + platform, framework = platform_framework.value + + # Set base core configuration + CORE.data[KEY_CORE] = { + KEY_TARGET_PLATFORM: platform.value, + KEY_TARGET_FRAMEWORK: framework.value, + } + + # Update with any additional core data + if core_data: + CORE.data[KEY_CORE].update(core_data) + + # Set platform-specific data + if platform_data: + CORE.data[platform.value] = platform_data + + config.path_context.set([]) + final_validate.full_config.set(Config()) + + yield setter + + +@pytest.fixture +def set_component_config() -> Callable[[str, Any], None]: + """ + Fixture to set a component configuration in the mock config. + This must be used after the core configuration has been set up. + """ + + def setter(name: str, value: Any) -> None: + final_validate.full_config.get()[name] = value + + return setter + + @pytest.fixture def component_fixture_path(request: pytest.FixtureRequest) -> Callable[[str], Path]: """Return a function to get absolute paths relative to the component's fixtures directory.""" @@ -60,7 +125,7 @@ def component_config_path(request: pytest.FixtureRequest) -> Callable[[str], Pat @pytest.fixture def generate_main() -> Generator[Callable[[str | Path], str]]: - """Generates the C++ main.cpp file and returns it in string form.""" + """Generates the C++ main.cpp from a given yaml file and returns it in string form.""" def generator(path: str | Path) -> str: CORE.config_path = str(path) @@ -69,5 +134,3 @@ def generate_main() -> Generator[Callable[[str | Path], str]]: return CORE.cpp_main_section yield generator - - CORE.reset() diff --git a/tests/component_tests/deep_sleep/__init__.py b/tests/component_tests/deep_sleep/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/image/__init__.py b/tests/component_tests/image/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/mipi_spi/__init__.py b/tests/component_tests/mipi_spi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/mipi_spi/fixtures/lvgl.yaml b/tests/component_tests/mipi_spi/fixtures/lvgl.yaml new file mode 100644 index 0000000000..bc800e1090 --- /dev/null +++ b/tests/component_tests/mipi_spi/fixtures/lvgl.yaml @@ -0,0 +1,25 @@ +esphome: + name: c3-7735 + +esp32: + board: lolin_c3_mini + +spi: + mosi_pin: + number: GPIO2 + ignore_strapping_warning: true + clk_pin: GPIO1 + +display: + - platform: mipi_spi + data_rate: 20MHz + model: st7735 + cs_pin: + number: GPIO8 + ignore_strapping_warning: true + dc_pin: + number: GPIO3 + reset_pin: + number: GPIO4 + +lvgl: diff --git a/tests/component_tests/mipi_spi/fixtures/native.yaml b/tests/component_tests/mipi_spi/fixtures/native.yaml new file mode 100644 index 0000000000..6962ac25c7 --- /dev/null +++ b/tests/component_tests/mipi_spi/fixtures/native.yaml @@ -0,0 +1,20 @@ +esphome: + name: jc3636w518 + +esp32: + board: esp32-s3-devkitc-1 + framework: + type: esp-idf + +psram: + mode: octal + +spi: + id: display_qspi + type: quad + clk_pin: 9 + data_pins: [11, 12, 13, 14] + +display: + - platform: mipi_spi + model: jc3636w518 diff --git a/tests/component_tests/mipi_spi/test_init.py b/tests/component_tests/mipi_spi/test_init.py new file mode 100644 index 0000000000..96abad02ad --- /dev/null +++ b/tests/component_tests/mipi_spi/test_init.py @@ -0,0 +1,387 @@ +"""Tests for mpip_spi configuration validation.""" + +from collections.abc import Callable +from pathlib import Path +from typing import Any + +import pytest + +from esphome import config_validation as cv +from esphome.components.esp32 import ( + KEY_BOARD, + KEY_ESP32, + KEY_VARIANT, + VARIANT_ESP32, + VARIANT_ESP32S3, + VARIANTS, +) +from esphome.components.esp32.gpio import validate_gpio_pin +from esphome.components.mipi_spi.display import ( + CONF_BUS_MODE, + CONF_NATIVE_HEIGHT, + CONFIG_SCHEMA, + FINAL_VALIDATE_SCHEMA, + MODELS, + dimension_schema, +) +from esphome.const import ( + CONF_DC_PIN, + CONF_DIMENSIONS, + CONF_HEIGHT, + CONF_INIT_SEQUENCE, + CONF_WIDTH, + PlatformFramework, +) +from esphome.core import CORE +from esphome.pins import internal_gpio_pin_number +from esphome.types import ConfigType +from tests.component_tests.types import SetCoreConfigCallable + + +def run_schema_validation(config: ConfigType) -> None: + """Run schema validation on a configuration.""" + FINAL_VALIDATE_SCHEMA(CONFIG_SCHEMA(config)) + + +@pytest.fixture +def choose_variant_with_pins() -> Callable[..., None]: + """ + Set the ESP32 variant for the given model based on pins. For ESP32 only since the other platforms + do not have variants. + """ + + def chooser(*pins: int | str | None) -> None: + for v in VARIANTS: + try: + CORE.data[KEY_ESP32][KEY_VARIANT] = v + for pin in pins: + if pin is not None: + pin = internal_gpio_pin_number(pin) + validate_gpio_pin(pin) + return + except cv.Invalid: + continue + + return chooser + + +@pytest.mark.parametrize( + ("config", "error_match"), + [ + pytest.param( + "a string", + "expected a dictionary", + id="invalid_string_config", + ), + pytest.param( + {"id": "display_id"}, + r"required key not provided @ data\['model'\]", + id="missing_model", + ), + pytest.param( + {"id": "display_id", "model": "custom", "init_sequence": [[0x36, 0x01]]}, + r"required key not provided @ data\['dimensions'\]", + id="missing_dimensions", + ), + pytest.param( + { + "model": "custom", + "dc_pin": 18, + "dimensions": {"width": 320, "height": 240}, + }, + r"required key not provided @ data\['init_sequence'\]", + id="missing_init_sequence", + ), + pytest.param( + { + "id": "display_id", + "model": "custom", + "dimensions": {"width": 320, "height": 240}, + "draw_rounding": 13, + "init_sequence": [[0xA0, 0x01]], + }, + r"value must be a power of two for dictionary value @ data\['draw_rounding'\]", + id="invalid_draw_rounding", + ), + ], +) +def test_basic_configuration_errors( + config: str | ConfigType, + error_match: str, + set_core_config: SetCoreConfigCallable, +) -> None: + """Test basic configuration validation errors""" + + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32}, + ) + + with pytest.raises(cv.Invalid, match=error_match): + run_schema_validation(config) + + +@pytest.mark.parametrize( + ("rounding", "config", "error_match"), + [ + pytest.param( + 4, + {"width": 320}, + r"required key not provided @ data\['height'\]", + id="missing_height", + ), + pytest.param( + 32, + {"width": 320, "height": 111}, + "Dimensions and offsets must be divisible by 32", + id="dimensions_not_divisible", + ), + ], +) +def test_dimension_validation( + rounding: int, + config: ConfigType, + error_match: str, + set_core_config: SetCoreConfigCallable, +) -> None: + """Test dimension-related validation errors""" + + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32}, + ) + + with pytest.raises(cv.Invalid, match=error_match): + dimension_schema(rounding)(config) + + +@pytest.mark.parametrize( + ("config", "error_match"), + [ + pytest.param( + { + "model": "JC3248W535", + "transform": {"mirror_x": False, "mirror_y": True, "swap_xy": True}, + }, + "Axis swapping not supported by this model", + id="axis_swapping_not_supported", + ), + pytest.param( + { + "model": "custom", + "dimensions": {"width": 320, "height": 240}, + "transform": {"mirror_x": False, "mirror_y": True, "swap_xy": False}, + "init_sequence": [[0x36, 0x01]], + }, + r"transform is not supported when MADCTL \(0X36\) is in the init sequence", + id="transform_with_madctl", + ), + pytest.param( + { + "model": "custom", + "dimensions": {"width": 320, "height": 240}, + "init_sequence": [[0x3A, 0x01]], + }, + r"PIXFMT \(0X3A\) should not be in the init sequence, it will be set automatically", + id="pixfmt_in_init_sequence", + ), + ], +) +def test_transform_and_init_sequence_errors( + config: ConfigType, + error_match: str, + set_core_config: SetCoreConfigCallable, +) -> None: + """Test transform and init sequence validation errors""" + + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32}, + ) + + with pytest.raises(cv.Invalid, match=error_match): + run_schema_validation(config) + + +@pytest.mark.parametrize( + ("config", "error_match"), + [ + pytest.param( + {"model": "t4-s3", "dc_pin": 18}, + "DC pin is not supported in quad mode", + id="dc_pin_not_supported_quad_mode", + ), + pytest.param( + {"model": "t4-s3", "color_depth": 18}, + "Unknown value '18', valid options are '16', '16bit", + id="invalid_color_depth_t4_s3", + ), + pytest.param( + {"model": "t-embed", "color_depth": 24}, + "Unknown value '24', valid options are '16', '8", + id="invalid_color_depth_t_embed", + ), + pytest.param( + {"model": "ili9488"}, + "DC pin is required in single mode", + id="dc_pin_required_single_mode", + ), + pytest.param( + {"model": "wt32-sc01-plus", "brightness": 128}, + r"extra keys not allowed @ data\['brightness'\]", + id="brightness_not_supported", + ), + pytest.param( + {"model": "T-DISPLAY-S3-PRO"}, + "PSRAM is required for this display", + id="psram_required", + ), + ], +) +def test_esp32s3_specific_errors( + config: ConfigType, + error_match: str, + set_core_config: SetCoreConfigCallable, +) -> None: + """Test ESP32-S3 specific configuration errors""" + + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32S3}, + ) + + with pytest.raises(cv.Invalid, match=error_match): + run_schema_validation(config) + + +def test_framework_specific_errors( + set_core_config: SetCoreConfigCallable, +) -> None: + """Test framework-specific configuration errors""" + + set_core_config( + PlatformFramework.ESP32_ARDUINO, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32}, + ) + + with pytest.raises( + cv.Invalid, + match=r"This feature is only available with frameworks \['esp-idf'\]", + ): + run_schema_validation({"model": "wt32-sc01-plus"}) + + +def test_custom_model_with_all_options( + set_core_config: SetCoreConfigCallable, +) -> None: + """Test custom model configuration with all available options.""" + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32S3}, + ) + + run_schema_validation( + { + "model": "custom", + "pixel_mode": "18bit", + "color_depth": 8, + "id": "display_id", + "byte_order": "little_endian", + "bus_mode": "single", + "color_order": "rgb", + "dc_pin": 11, + "reset_pin": 12, + "enable_pin": 13, + "cs_pin": 14, + "init_sequence": [[0xA0, 0x01]], + "dimensions": { + "width": 320, + "height": 240, + "offset_width": 32, + "offset_height": 32, + }, + "invert_colors": True, + "transform": {"mirror_x": True, "mirror_y": True, "swap_xy": False}, + "spi_mode": "mode0", + "data_rate": "40MHz", + "use_axis_flips": True, + "draw_rounding": 4, + "spi_16": True, + "buffer_size": 0.25, + } + ) + + +def test_all_predefined_models( + set_core_config: SetCoreConfigCallable, + set_component_config: Callable[[str, Any], None], + choose_variant_with_pins: Callable[..., None], +) -> None: + """Test all predefined display models validate successfully with appropriate defaults.""" + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={KEY_BOARD: "esp32dev", KEY_VARIANT: VARIANT_ESP32S3}, + ) + + # Enable PSRAM which is required for some models + set_component_config("psram", True) + + # Test all models, providing default values where necessary + for name, model in MODELS.items(): + config = {"model": name} + + # Get the pins required by this model and find a compatible variant + pins = [ + pin + for pin in [ + model.get_default(pin, None) + for pin in ("dc_pin", "reset_pin", "cs_pin") + ] + if pin is not None + ] + choose_variant_with_pins(pins) + + # Add required fields that don't have defaults + if ( + not model.get_default(CONF_DC_PIN) + and model.get_default(CONF_BUS_MODE) != "quad" + ): + config[CONF_DC_PIN] = 14 + if not model.get_default(CONF_NATIVE_HEIGHT): + config[CONF_DIMENSIONS] = {CONF_HEIGHT: 240, CONF_WIDTH: 320} + if model.initsequence is None: + config[CONF_INIT_SEQUENCE] = [[0xA0, 0x01]] + + run_schema_validation(config) + + +def test_native_generation( + generate_main: Callable[[str | Path], str], + component_fixture_path: Callable[[str], Path], +) -> None: + """Test code generation for display.""" + + main_cpp = generate_main(component_fixture_path("native.yaml")) + assert ( + "mipi_spi::MipiSpiBuffer()" + in main_cpp + ) + assert "set_init_sequence({240, 1, 8, 242" in main_cpp + assert "show_test_card();" in main_cpp + assert "set_write_only(true);" in main_cpp + + +def test_lvgl_generation( + generate_main: Callable[[str | Path], str], + component_fixture_path: Callable[[str], Path], +) -> None: + """Test LVGL generation configuration.""" + + main_cpp = generate_main(component_fixture_path("lvgl.yaml")) + assert ( + "mipi_spi::MipiSpi();" + in main_cpp + ) + assert "set_init_sequence({1, 0, 10, 255, 177" in main_cpp + assert "show_test_card();" not in main_cpp + assert "set_auto_clear(false);" in main_cpp diff --git a/tests/component_tests/ota/__init__.py b/tests/component_tests/ota/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/packages/__init__.py b/tests/component_tests/packages/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/sensor/__init__.py b/tests/component_tests/sensor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/text/__init__.py b/tests/component_tests/text/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/text_sensor/__init__.py b/tests/component_tests/text_sensor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/types.py b/tests/component_tests/types.py new file mode 100644 index 0000000000..72b8be4503 --- /dev/null +++ b/tests/component_tests/types.py @@ -0,0 +1,21 @@ +"""Type definitions for component tests.""" + +from __future__ import annotations + +from typing import Protocol + +from esphome.const import PlatformFramework +from esphome.types import ConfigType + + +class SetCoreConfigCallable(Protocol): + """Protocol for the set_core_config fixture setter function.""" + + def __call__( # noqa: E704 + self, + platform_framework: PlatformFramework, + /, + *, + core_data: ConfigType | None = None, + platform_data: ConfigType | None = None, + ) -> None: ... diff --git a/tests/component_tests/web_server/__init__.py b/tests/component_tests/web_server/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/components/mipi_spi/test-esp32-2432s028.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-esp32-2432s028.esp32-s3-idf.yaml deleted file mode 100644 index a28776798c..0000000000 --- a/tests/components/mipi_spi/test-esp32-2432s028.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: ESP32-2432S028 diff --git a/tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml deleted file mode 100644 index 02b8f78d58..0000000000 --- a/tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: JC3248W535 diff --git a/tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml deleted file mode 100644 index 147d4833ac..0000000000 --- a/tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml +++ /dev/null @@ -1,19 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 36 - data_pins: - - number: 40 - - number: 41 - - number: 42 - - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: JC3636W518 diff --git a/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml new file mode 100644 index 0000000000..e0f65a3a6a --- /dev/null +++ b/tests/components/mipi_spi/test-lvgl.esp32-s3-idf.yaml @@ -0,0 +1,18 @@ +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + +spi: + - id: spi_single + clk_pin: + number: ${clk_pin} + mosi_pin: + number: ${mosi_pin} + +display: + - platform: mipi_spi + model: t-display-s3-pro + +lvgl: + +psram: diff --git a/tests/components/mipi_spi/test-pico-restouch-lcd-35.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-pico-restouch-lcd-35.esp32-s3-idf.yaml deleted file mode 100644 index 8d96f31fd5..0000000000 --- a/tests/components/mipi_spi/test-pico-restouch-lcd-35.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spi: - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: Pico-ResTouch-LCD-3.5 diff --git a/tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml deleted file mode 100644 index 98f6955bf3..0000000000 --- a/tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: S3BOX diff --git a/tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml deleted file mode 100644 index 11ad869d54..0000000000 --- a/tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: S3BOXLITE diff --git a/tests/components/mipi_spi/test-t-display-s3-amoled-plus.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-display-s3-amoled-plus.esp32-s3-idf.yaml deleted file mode 100644 index dc328f950c..0000000000 --- a/tests/components/mipi_spi/test-t-display-s3-amoled-plus.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spi: - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: T-DISPLAY-S3-AMOLED-PLUS diff --git a/tests/components/mipi_spi/test-t-display-s3-amoled.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-display-s3-amoled.esp32-s3-idf.yaml deleted file mode 100644 index f0432270dc..0000000000 --- a/tests/components/mipi_spi/test-t-display-s3-amoled.esp32-s3-idf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - number: 40 - - number: 41 - - number: 42 - - number: 43 - -display: - - platform: mipi_spi - model: T-DISPLAY-S3-AMOLED diff --git a/tests/components/mipi_spi/test-t-display-s3-pro.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-display-s3-pro.esp32-s3-idf.yaml deleted file mode 100644 index 5cda38e096..0000000000 --- a/tests/components/mipi_spi/test-t-display-s3-pro.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spi: - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 40 - -display: - - platform: mipi_spi - model: T-DISPLAY-S3-PRO diff --git a/tests/components/mipi_spi/test-t-display-s3.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-display-s3.esp32-s3-idf.yaml deleted file mode 100644 index 144bde8366..0000000000 --- a/tests/components/mipi_spi/test-t-display-s3.esp32-s3-idf.yaml +++ /dev/null @@ -1,37 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - -display: - - platform: mipi_spi - model: T-DISPLAY-S3 diff --git a/tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml deleted file mode 100644 index 39339b5ae2..0000000000 --- a/tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: T-DISPLAY diff --git a/tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml deleted file mode 100644 index 6c9edb25b3..0000000000 --- a/tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -spi: - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 40 - -display: - - platform: mipi_spi - model: T-EMBED diff --git a/tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml deleted file mode 100644 index 46eaedb7cb..0000000000 --- a/tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml +++ /dev/null @@ -1,41 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 0 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: spi_id_3 - interface: any - clk_pin: 8 - mosi_pin: 9 - -display: - - platform: mipi_spi - model: T4-S3 diff --git a/tests/components/mipi_spi/test-wt32-sc01-plus.esp32-s3-idf.yaml b/tests/components/mipi_spi/test-wt32-sc01-plus.esp32-s3-idf.yaml deleted file mode 100644 index 3efb05ec89..0000000000 --- a/tests/components/mipi_spi/test-wt32-sc01-plus.esp32-s3-idf.yaml +++ /dev/null @@ -1,37 +0,0 @@ -spi: - - id: quad_spi - type: quad - interface: spi3 - clk_pin: - number: 47 - data_pins: - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - - id: octal_spi - type: octal - interface: hardware - clk_pin: - number: 9 - data_pins: - - 36 - - 37 - - 38 - - 39 - - allow_other_uses: true - number: 40 - - allow_other_uses: true - number: 41 - - allow_other_uses: true - number: 42 - - allow_other_uses: true - number: 43 - -display: - - platform: mipi_spi - model: WT32-SC01-PLUS