mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 07:20:24 +00:00
Address review: fan-out guard, refusal warning, sighting-less connect hint
- The connected fan-out stops if a node tears the link down mid-loop (latent until a backend settles a teardown synchronously) - A synchronously refused disconnect logs its own warning - a backend/ client state divergence is no longer the most quietly logged outcome - An action-initiated connect before any sighting warns that the public address type is assumed (an opaque repeated failure becomes self-diagnosing); the connect() doc no longer mentions a config key that does not exist - The explicit-connections ledger test asserts the exact charged count; the host-test override comment names the current gate macro
This commit is contained in:
@@ -42,6 +42,7 @@ bool BLEClient::parse_device(const ble_device_base::ESPBTDevice &device) {
|
||||
return false;
|
||||
// The sighting is the source of truth for the address type.
|
||||
this->address_type_ = device.get_address_type();
|
||||
this->address_type_known_ = true;
|
||||
if (!this->enabled || !this->auto_connect_ || this->state_ != State::IDLE)
|
||||
return true;
|
||||
if (this->hold_off_ms_ != 0 && millis() - this->hold_off_start_ < this->hold_off_ms_)
|
||||
@@ -56,6 +57,11 @@ void BLEClient::connect() {
|
||||
// An absent peer can inhibit scanning for the backend's full connect
|
||||
// timeout, so this is worth a breadcrumb - but it is a supported action.
|
||||
ESP_LOGI(TAG, "[%s] Connecting on request", this->address_str_);
|
||||
if (!this->address_type_known_) {
|
||||
// Legacy parity: without a sighting the address type defaults to
|
||||
// public, which never matches a random-static peer.
|
||||
ESP_LOGW(TAG, "[%s] No sighting yet; assuming a public address type", this->address_str_);
|
||||
}
|
||||
this->attempt_connect_();
|
||||
}
|
||||
|
||||
@@ -81,9 +87,10 @@ void BLEClient::disconnect() {
|
||||
this->cancel_requested_ = true;
|
||||
int err = this->backend_->gatt_disconnect();
|
||||
if (err != 0) {
|
||||
// Refused synchronously (states diverged; the backend is already down):
|
||||
// settle through the normal path, carrying the real code so the log is
|
||||
// distinguishable from a clean teardown.
|
||||
// Refused synchronously: the backend and the client disagreed about the
|
||||
// link state. Worth a warning of its own - the settle below then runs
|
||||
// the same deliberate-cancel path as a clean teardown.
|
||||
ESP_LOGW(TAG, "[%s] Disconnect refused, err=%d; settling locally", this->address_str_, err);
|
||||
this->on_connection_state(false, 0, err);
|
||||
}
|
||||
}
|
||||
@@ -149,6 +156,9 @@ void BLEClient::on_service_discovery_done(int error) {
|
||||
auto table = this->backend_->get_service_table();
|
||||
for (auto *node : this->nodes_) {
|
||||
node->on_connected(table);
|
||||
if (this->state_ != State::CONNECTED) {
|
||||
return; // A node tore the link down mid-fan-out; the teardown settled.
|
||||
}
|
||||
}
|
||||
this->backend_->release_services();
|
||||
this->consecutive_failures_ = 0;
|
||||
|
||||
@@ -73,8 +73,8 @@ class BLEClient : public Component,
|
||||
bool connected() const { return this->state_ == State::CONNECTED; }
|
||||
bool idle() const { return this->state_ == State::IDLE; }
|
||||
|
||||
/// Action-initiated connect (no sighting needed; uses the last captured or
|
||||
/// configured address type). No-op unless idle.
|
||||
/// Action-initiated connect (no sighting needed; uses the last captured
|
||||
/// address type, public until a sighting arrives). No-op unless idle.
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
@@ -147,6 +147,8 @@ class BLEClient : public Component,
|
||||
// Group 6: 1-byte types
|
||||
State state_{State::IDLE};
|
||||
uint8_t address_type_{0}; // BLE_ADDR_TYPE_*, captured from the sighting
|
||||
// Distinguishes a captured public type from the never-sighted default.
|
||||
bool address_type_known_{false};
|
||||
bool auto_connect_{true};
|
||||
// A user-initiated teardown in flight; its failure report is not a
|
||||
// connect failure and must not feed the backoff.
|
||||
|
||||
@@ -186,7 +186,9 @@ def test_esp32_explicit_connections_claim_gatt_slots(
|
||||
# generated ones; dev historically let them evade the budget.
|
||||
set_core_config(PlatformFramework.ESP32_IDF)
|
||||
bluetooth_proxy.CONFIG_SCHEMA({"active": True, "connections": [{}, {}]})
|
||||
assert "bluetooth_proxy" in bluetooth_connection._ledger().consumers
|
||||
# Exact match (one entry per slot): catches a missed charge and a
|
||||
# double charge alike.
|
||||
assert bluetooth_connection._ledger().consumers == ["bluetooth_proxy"] * 2
|
||||
|
||||
|
||||
def test_hub_source_filter_covers_every_hub_platform() -> None:
|
||||
|
||||
@@ -3,8 +3,9 @@ from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# close_service_batch compiles only under BLUETOOTH_CONNECTION_HAS_GATT;
|
||||
# emit the backend define so the host build exercises it.
|
||||
# close_service_batch compiles only under BLUETOOTH_CONNECTION_SERVES_PROXY
|
||||
# (= USE_BLE_GATT_CLIENT && USE_BLUETOOTH_PROXY); emit both so the host
|
||||
# build exercises it.
|
||||
async def to_code_testing(config):
|
||||
# These defines are global to the merged host test binary; safe
|
||||
# because no co-compiled test observes them.
|
||||
|
||||
Reference in New Issue
Block a user