Name the mixed-client invariant and fold the promote loop

has_legacy_nodes_() lives next to the invariant it derives from; both
bailout outcomes reach one promotion loop; the carve assert relaxes to the
descending chain the comment claims; the notify op string names the IDF
call like its siblings.
This commit is contained in:
J. Nick Koston
2026-08-12 19:03:16 -05:00
parent 7ad51b4b8f
commit b00ad7ca8a
4 changed files with 27 additions and 27 deletions
+21 -24
View File
@@ -238,35 +238,32 @@ bool BLEClient::handle_gatt_search_cmpl_(esp_gatt_status_t status) {
// Stack-owned; nodes copy their handles during on_connected().
bluetooth_connection::BluedroidServiceTable table;
if (!table.build(this->gattc_if_, this->conn_id_, service_total, this->connection_index_)) {
if (this->nodes_.size() > this->gatt_nodes_.size()) {
// Only the table build failed; legacy nodes read the base's services_
// and keep the link. Gatt nodes catch the next connection (promoted so
// the release condition still fires).
ESP_LOGW(TAG, "[%s] Service table build failed; gatt nodes skip this connection", this->address_str());
for (auto *node : this->gatt_nodes_)
node->node_state = espbt::ClientState::ESTABLISHED;
return true;
}
ESP_LOGW(TAG, "[%s] Service table build failed; treating as failed discovery", this->address_str());
this->gatt_backoff_.register_failure(this->address_str());
this->disconnect();
return false;
}
this->gatt_connected_ = true;
auto view = table.view();
for (auto *node : this->gatt_nodes_) {
node->on_connected(view);
if (this->state() != espbt::ClientState::ESTABLISHED) {
// The node tore the link down; remaining nodes get on_disconnected
// with no preceding on_connected, so leave a trace of why.
ESP_LOGW(TAG, "[%s] A node aborted the connection during setup", this->address_str());
if (!this->has_legacy_nodes_()) {
ESP_LOGW(TAG, "[%s] Service table build failed; treating as failed discovery", this->address_str());
this->gatt_backoff_.register_failure(this->address_str());
this->disconnect();
return false;
}
// Only the table build failed; legacy nodes read the base's services_
// and keep the link. Gatt nodes catch the next connection.
ESP_LOGW(TAG, "[%s] Service table build failed; gatt nodes skip this connection", this->address_str());
} else {
this->gatt_connected_ = true;
auto view = table.view();
for (auto *node : this->gatt_nodes_) {
node->on_connected(view);
if (this->state() != espbt::ClientState::ESTABLISHED) {
// The node tore the link down; remaining nodes get on_disconnected
// with no preceding on_connected, so leave a trace of why.
ESP_LOGW(TAG, "[%s] A node aborted the connection during setup", this->address_str());
return false;
}
}
this->gatt_backoff_.reset();
}
// Promote so the legacy release condition can fire.
for (auto *node : this->gatt_nodes_)
node->node_state = espbt::ClientState::ESTABLISHED;
this->gatt_backoff_.reset();
return true;
}
@@ -338,7 +335,7 @@ int BLEClient::notify_characteristic(uint16_t handle, bool enable) {
esp_err_t err = this->register_for_notify(handle);
if (err == ESP_OK)
this->pending_gatt_regs_[this->pending_gatt_reg_count_++] = handle;
return this->check_and_log_error_("register_for_notify", err);
return this->check_and_log_error_("esp_ble_gattc_register_for_notify", err);
}
return this->check_and_log_error_("esp_ble_gattc_unregister_for_notify",
esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, handle));
+3 -1
View File
@@ -85,8 +85,10 @@ class BLEClient final : public BLEClientBase {
static constexpr uint8_t MAX_PENDING_NOTIFY_REGS = 4;
// Nodes on the neutral surface; fed the translated callbacks and
// auto-established after the on_connected fan-out.
// auto-established after the on_connected fan-out. Every gatt node is
// also in nodes_ (registration pushes into both).
StaticVector<BLEClientNode *, ESPHOME_BLE_CLIENT_MAX_NODES> gatt_nodes_;
bool has_legacy_nodes_() const { return this->nodes_.size() > this->gatt_nodes_.size(); }
// Reconnect backoff after materializer failures.
ConnectBackoff gatt_backoff_;
// Bridge-initiated notify registrations awaiting REG_FOR_NOTIFY_EVT.
@@ -117,7 +117,7 @@ bool BluedroidServiceTable::build(esp_gatt_if_t gattc_if, uint16_t conn_id, uint
// The arrays share one block; carving stays aligned because each struct's
// strictest member is the UUID and array sizes are multiples of it.
static_assert(alignof(ble_device_base::GattService) == alignof(ble_device_base::GattCharacteristic) &&
static_assert(alignof(ble_device_base::GattService) >= alignof(ble_device_base::GattCharacteristic) &&
alignof(ble_device_base::GattCharacteristic) >= alignof(ble_device_base::GattDescriptor));
size_t svc_bytes = this->service_total_ * sizeof(ble_device_base::GattService);
size_t char_bytes = char_total * sizeof(ble_device_base::GattCharacteristic);
@@ -1,5 +1,6 @@
# Advertisement-only proxy on esp32 by explicit choice: no GATT backend is
# compiled (USE_BLE_GATT_CLIENT unset), which pins the USE_BLUETOOTH_PROXY_CONNECTIONS gating and the
# compiled (USE_BLE_GATT_CLIENT unset), which pins the
# USE_BLUETOOTH_PROXY_CONNECTIONS gating and the
# address-scoped maintenance path that a connections build never exercises.
# Under batch grouping the active default build is what runs; the standalone
# compile of this fixture is what exercises the passive gating.