From 5453835963c1df898c31f38207d7c52d70d730f4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Jun 2025 18:09:53 +0200 Subject: [PATCH 1/5] make ble client disable/enable smarter --- .../esp32_ble_client/ble_client_base.cpp | 19 +++++++++++++------ .../esp32_ble_client/ble_client_base.h | 6 ++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 115d785eae..ef1d427113 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -22,6 +22,19 @@ void BLEClientBase::setup() { this->connection_index_ = connection_index++; } +void BLEClientBase::set_state(espbt::ClientState st) { + ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st); + ESPBTClient::set_state(st); + + // Disable loop when idle AND address is not set (unused connection slot) + if (st == espbt::ClientState::IDLE && this->address_ == 0) { + this->disable_loop(); + } else if (st == espbt::ClientState::READY_TO_CONNECT || st == espbt::ClientState::INIT) { + // Enable loop when we need to initialize or connect + this->enable_loop(); + } +} + void BLEClientBase::loop() { if (!esp32_ble::global_ble->is_active()) { this->set_state(espbt::ClientState::INIT); @@ -36,12 +49,6 @@ void BLEClientBase::loop() { this->set_state(espbt::ClientState::IDLE); } - // If address is 0, this connection is not in use - if (this->address_ == 0) { - this->disable_loop(); - return; - } - // READY_TO_CONNECT means we have discovered the device // and the scanner has been stopped by the tracker. if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 69c7c31ad8..814a9664d9 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -63,10 +63,6 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { (uint8_t) (this->address_ >> 16) & 0xff, (uint8_t) (this->address_ >> 8) & 0xff, (uint8_t) (this->address_ >> 0) & 0xff); } - // Re-enable loop() when a non-zero address is assigned - if (address != 0) { - this->enable_loop(); - } } std::string address_str() const { return this->address_str_; } @@ -97,6 +93,8 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; } + void set_state(espbt::ClientState st) override; + protected: int gattc_if_; esp_bd_addr_t remote_bda_; From 7d314398e15f19bb19a7516a98abceb80789cdf0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Jun 2025 22:27:24 +0200 Subject: [PATCH 2/5] cleaner fix --- .../components/esp32_ble_client/ble_client_base.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index ef1d427113..9a8b0006bc 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -26,11 +26,8 @@ void BLEClientBase::set_state(espbt::ClientState st) { ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st); ESPBTClient::set_state(st); - // Disable loop when idle AND address is not set (unused connection slot) - if (st == espbt::ClientState::IDLE && this->address_ == 0) { - this->disable_loop(); - } else if (st == espbt::ClientState::READY_TO_CONNECT || st == espbt::ClientState::INIT) { - // Enable loop when we need to initialize or connect + if (st == espbt::ClientState::READY_TO_CONNECT) { + // Enable loop when we need to connect this->enable_loop(); } } @@ -51,9 +48,8 @@ void BLEClientBase::loop() { // READY_TO_CONNECT means we have discovered the device // and the scanner has been stopped by the tracker. - if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { - this->connect(); - } + elif (this->state_ == espbt::ClientState::READY_TO_CONNECT) { this->connect(); } + elif (this->state_ == espbt::ClientState::IDLE) { this->disable_loop(); } } float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } From 4c37c20d76147d0cae04f51fc35e89bebbcade68 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Jun 2025 22:29:21 +0200 Subject: [PATCH 3/5] cleaner fix --- .../components/esp32_ble_client/ble_client_base.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 9a8b0006bc..8ae1eb1bac 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -45,11 +45,16 @@ void BLEClientBase::loop() { } this->set_state(espbt::ClientState::IDLE); } - // READY_TO_CONNECT means we have discovered the device // and the scanner has been stopped by the tracker. - elif (this->state_ == espbt::ClientState::READY_TO_CONNECT) { this->connect(); } - elif (this->state_ == espbt::ClientState::IDLE) { this->disable_loop(); } + else if (this->state_ == espbt::ClientState::READY_TO_CONNECT) { + this->connect(); + } + // If its idle, we can disable the loop as set_state + // will enable it again when we need to connect. + else if (this->state_ == espbt::ClientState::IDLE) { + this->disable_loop(); + } } float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } From 969abc3f291c415cf920e9922e86a810aaed1ac1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Jun 2025 23:40:31 +0200 Subject: [PATCH 4/5] make sure components that disable in setup are disabled at start --- esphome/core/application.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 74208bbe22..8b8024c29b 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -244,11 +244,25 @@ void Application::teardown_components(uint32_t timeout_ms) { void Application::calculate_looping_components_() { for (auto *obj : this->components_) { - if (obj->has_overridden_loop()) + if (obj->has_overridden_loop()) { this->looping_components_.push_back(obj); + } + } + + // Partition components based on their current state + // Components that have already called disable_loop() during setup (state == LOOP_DONE) + // should start in the inactive section of the partition + this->looping_components_active_end_ = 0; + for (uint16_t i = 0; i < this->looping_components_.size(); i++) { + Component *comp = this->looping_components_[i]; + if ((comp->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { + // Component is active - swap it to the active section if needed + if (i != this->looping_components_active_end_) { + std::swap(this->looping_components_[i], this->looping_components_[this->looping_components_active_end_]); + } + this->looping_components_active_end_++; + } } - // Initially all components are active - this->looping_components_active_end_ = this->looping_components_.size(); } void Application::disable_component_loop_(Component *component) { From d8a7e9abc880497c7ae794f41cc670f075783839 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 17 Jun 2025 23:44:32 +0200 Subject: [PATCH 5/5] make sure components that disable in setup are disabled at start --- esphome/core/application.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 8b8024c29b..58df49f0f2 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -243,24 +243,22 @@ void Application::teardown_components(uint32_t timeout_ms) { } void Application::calculate_looping_components_() { + // First add all active components for (auto *obj : this->components_) { - if (obj->has_overridden_loop()) { + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { this->looping_components_.push_back(obj); } } - // Partition components based on their current state - // Components that have already called disable_loop() during setup (state == LOOP_DONE) - // should start in the inactive section of the partition - this->looping_components_active_end_ = 0; - for (uint16_t i = 0; i < this->looping_components_.size(); i++) { - Component *comp = this->looping_components_[i]; - if ((comp->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) { - // Component is active - swap it to the active section if needed - if (i != this->looping_components_active_end_) { - std::swap(this->looping_components_[i], this->looping_components_[this->looping_components_active_end_]); - } - this->looping_components_active_end_++; + this->looping_components_active_end_ = this->looping_components_.size(); + + // Then add all inactive (LOOP_DONE) components + // This handles components that called disable_loop() during setup, before this method runs + for (auto *obj : this->components_) { + if (obj->has_overridden_loop() && + (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) { + this->looping_components_.push_back(obj); } } }