From 7cb32f9546d230ea84ade39b823f37e996d2f1ef Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Aug 2026 09:31:21 -0500 Subject: [PATCH] Clean up iteration residue from the review rounds - Drop unused includes (ble_client_state.h, ) and the unused notify/indicate property constants (no CCCD path yet) - Note the frozen node-facing surface on the uncalled backend forwarders - Keep the legacy-only 'name' key out of the neutral schema - Move the slot-ledger test to tests/component_tests/bluetooth_connection - Comment accuracy fixes (choke-point wording, wrapped lines, backoff note) --- esphome/components/ble_client/__init__.py | 4 +++- .../components/ble_client/automation_gatt.h | 1 - .../components/ble_client/ble_client_gatt.cpp | 1 + .../components/ble_client/ble_client_gatt.h | 4 ++-- .../ble_device_base/ble_gatt_client.h | 2 -- .../bluetooth_connection.h | 5 ++--- .../ble_client/test_validation.py | 18 ++++-------------- .../bluetooth_connection/__init__.py | 0 .../bluetooth_connection/test_slot_ledger.py | 17 +++++++++++++++++ 9 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 tests/component_tests/bluetooth_connection/__init__.py create mode 100644 tests/component_tests/bluetooth_connection/test_slot_ledger.py diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index cf07290ce6..f994891bd3 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -151,7 +151,6 @@ _COMMON_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(BLEClient), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_NAME): cv.string, cv.Optional(CONF_AUTO_CONNECT, default=True): cv.boolean, cv.Optional(CONF_ON_CONNECT): automation.validate_automation( { @@ -178,6 +177,9 @@ def _esp32_config_schema() -> cv.All: return cv.All( _COMMON_SCHEMA.extend( { + # Accepted-but-unused legacy key; not propagated to the + # neutral schema. + cv.Optional(CONF_NAME): cv.string, cv.Optional(CONF_ON_PASSKEY_REQUEST): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( diff --git a/esphome/components/ble_client/automation_gatt.h b/esphome/components/ble_client/automation_gatt.h index f4e5db4a5c..c59d3c245e 100644 --- a/esphome/components/ble_client/automation_gatt.h +++ b/esphome/components/ble_client/automation_gatt.h @@ -12,7 +12,6 @@ #if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32) #include -#include #include #include "ble_client_gatt.h" diff --git a/esphome/components/ble_client/ble_client_gatt.cpp b/esphome/components/ble_client/ble_client_gatt.cpp index 9232e09ae9..045d901489 100644 --- a/esphome/components/ble_client/ble_client_gatt.cpp +++ b/esphome/components/ble_client/ble_client_gatt.cpp @@ -99,6 +99,7 @@ void BLEClient::on_connection_state(bool connected, uint16_t mtu, int error) { // Synchronous refusal: no discovery completion will follow. ESP_LOGW(TAG, "[%s] Service discovery refused", this->address_str_); this->register_failure_(); + // Deliberate teardown: its report must not charge the backoff again. this->disconnect(); } return; diff --git a/esphome/components/ble_client/ble_client_gatt.h b/esphome/components/ble_client/ble_client_gatt.h index 258b0b82c2..407692454e 100644 --- a/esphome/components/ble_client/ble_client_gatt.h +++ b/esphome/components/ble_client/ble_client_gatt.h @@ -15,7 +15,6 @@ #if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32) -#include "esphome/components/ble_device_base/ble_client_state.h" #include "esphome/components/ble_device_base/ble_device.h" #include "esphome/components/ble_device_base/ble_gatt_client.h" #include "esphome/components/bluetooth_connection/bluetooth_connection_gatt_backend.h" @@ -84,7 +83,8 @@ class BLEClient : public Component, /// continuations must leave that stack first. void run_later(std::function &&f) { this->defer(std::move(f)); } // NOLINT - // Backend ops for nodes and actions. + // Backend ops for nodes and actions. read/write_descriptor have no caller + // yet; kept as the frozen node-facing surface (like on_notify/on_read_result). int write_characteristic(uint16_t handle, const uint8_t *data, uint16_t len, bool response) { return this->backend_->write_characteristic(handle, data, len, response); } diff --git a/esphome/components/ble_device_base/ble_gatt_client.h b/esphome/components/ble_device_base/ble_gatt_client.h index fd07cac7f4..a03b8d26d1 100644 --- a/esphome/components/ble_device_base/ble_gatt_client.h +++ b/esphome/components/ble_device_base/ble_gatt_client.h @@ -161,8 +161,6 @@ static constexpr uint16_t CCCD_UUID = 0x2902; // exist on the other platforms). static constexpr uint8_t GATT_CHAR_PROP_WRITE_NO_RSP = 0x04; static constexpr uint8_t GATT_CHAR_PROP_WRITE = 0x08; -static constexpr uint8_t GATT_CHAR_PROP_NOTIFY = 0x10; -static constexpr uint8_t GATT_CHAR_PROP_INDICATE = 0x20; inline const GattService *find_service(const GattServiceTable &table, const ESPBTUUID &uuid) { for (uint16_t i = 0; i < table.service_count; i++) { diff --git a/esphome/components/bluetooth_connection/bluetooth_connection.h b/esphome/components/bluetooth_connection/bluetooth_connection.h index e1700c2afd..a69f2cf7db 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection.h @@ -22,9 +22,8 @@ // request handlers, and the Bluedroid in-place streamer all gate on it. // Builds without a compiled backend get the clean-error handlers (a // passive proxy alongside a backend consumer compiles the real ones); -// address-scoped -// maintenance (unpair, cache clear) still works there through the -// per-platform free functions below. Backend-only builds (a dedicated-backend +// address-scoped maintenance (unpair, cache clear) still works there through +// the per-platform free functions below. Backend-only builds (a dedicated-backend // consumer without bluetooth_proxy) compile none of this API surface. #if defined(USE_BLE_GATT_CLIENT) && defined(USE_BLUETOOTH_PROXY) #define BLUETOOTH_CONNECTION_SERVES_PROXY diff --git a/tests/component_tests/ble_client/test_validation.py b/tests/component_tests/ble_client/test_validation.py index b49e83506c..17e8c883a1 100644 --- a/tests/component_tests/ble_client/test_validation.py +++ b/tests/component_tests/ble_client/test_validation.py @@ -22,6 +22,7 @@ from esphome.const import ( KEY_CORE, KEY_TARGET_PLATFORM, PLATFORM_ESP32, + PLATFORM_RP2, ) from esphome.core import CORE from esphome.types import ConfigType @@ -29,8 +30,8 @@ from esphome.types import ConfigType @pytest.fixture(autouse=True) def esp32_platform() -> None: - # The node platforms gate on only_on_esp32 now (the neutral engine has no - # raw-gattc nodes); these schema tests exercise the esp32 arm. + # The raw-gattc node family gates through BLE_CLIENT_SCHEMA's + # _legacy_engine_only choke point; these schema tests exercise the esp32 arm. CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = PLATFORM_ESP32 @@ -98,21 +99,10 @@ def test_notify_unchanged_without_on_notify() -> None: assert notify_from_on_notify(config)[CONF_NOTIFY] is False -def test_gatt_slot_ledger_rejects_overcommit_on_rp2() -> None: - # Suggestion 4: the cross-component cap must reject two claims on rp2. - from esphome.components import bluetooth_connection - - CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = "rp2" - bluetooth_connection.consume_gatt_slot("bluetooth_proxy")({}) - bluetooth_connection.consume_gatt_slot("ble_client")({}) - with pytest.raises(cv.Invalid, match="supports at most 1 GATT client"): - bluetooth_connection.FINAL_VALIDATE_SCHEMA({}) - - def test_legacy_node_choke_point_rejects_other_platforms() -> None: from esphome.components import ble_client from esphome.core import ID - CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = "rp2" + CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = PLATFORM_RP2 with pytest.raises(cv.Invalid, match="not been migrated"): ble_client._legacy_engine_only(ID("x")) diff --git a/tests/component_tests/bluetooth_connection/__init__.py b/tests/component_tests/bluetooth_connection/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/bluetooth_connection/test_slot_ledger.py b/tests/component_tests/bluetooth_connection/test_slot_ledger.py new file mode 100644 index 0000000000..ad169e5497 --- /dev/null +++ b/tests/component_tests/bluetooth_connection/test_slot_ledger.py @@ -0,0 +1,17 @@ +"""Tests for the cross-component GATT slot ledger.""" + +import pytest + +from esphome import config_validation as cv +from esphome.components import bluetooth_connection +from esphome.const import KEY_CORE, KEY_TARGET_PLATFORM, PLATFORM_RP2 +from esphome.core import CORE + + +def test_gatt_slot_ledger_rejects_overcommit_on_rp2() -> None: + # The cross-component cap must reject two claims on rp2. + CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = PLATFORM_RP2 + bluetooth_connection.consume_gatt_slot("bluetooth_proxy")({}) + bluetooth_connection.consume_gatt_slot("ble_client")({}) + with pytest.raises(cv.Invalid, match="supports at most 1 GATT client"): + bluetooth_connection.FINAL_VALIDATE_SCHEMA({})