diff --git a/esphome/components/ble_client/ble_client_gatt.h b/esphome/components/ble_client/ble_client_gatt.h index 3291a8e2ef..4b0d72e545 100644 --- a/esphome/components/ble_client/ble_client_gatt.h +++ b/esphome/components/ble_client/ble_client_gatt.h @@ -83,15 +83,22 @@ 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. read/write_descriptor have no caller - // yet; kept as the frozen node-facing surface (like on_notify/on_read_result). + // Backend ops for nodes and actions - the frozen node-facing surface. + // Only write_characteristic has an in-tree caller today; the rest exist so + // the first migrated node codes against a complete interface (subscribing + // means notify_characteristic plus a write_descriptor on the CCCD - the + // backend contract keeps the CCCD write the caller's responsibility). int write_characteristic(uint16_t handle, const uint8_t *data, uint16_t len, bool response) { return this->backend_->write_characteristic(handle, data, len, response); } int read_characteristic(uint16_t handle) { return this->backend_->read_characteristic(handle); } + int read_descriptor(uint16_t handle) { return this->backend_->read_descriptor(handle); } int write_descriptor(uint16_t handle, const uint8_t *data, uint16_t len) { return this->backend_->write_descriptor(handle, data, len); } + int notify_characteristic(uint16_t handle, bool enable) { + return this->backend_->notify_characteristic(handle, enable); + } // Automation callback registration. template void add_on_connect_callback(F &&callback) { diff --git a/esphome/components/bluetooth_connection/__init__.py b/esphome/components/bluetooth_connection/__init__.py index d05e8369e4..6c8da1da6e 100644 --- a/esphome/components/bluetooth_connection/__init__.py +++ b/esphome/components/bluetooth_connection/__init__.py @@ -36,10 +36,10 @@ CODEOWNERS = ["@bdraco", "@jesserockz"] bluetooth_connection_ns = cg.esphome_ns.namespace("bluetooth_connection") -# arduino-pico's prebuilt BTstack is compiled with MAX_NR_GATT_CLIENTS 1; -# raising this needs an upstream change (the layer itself supports N). DOMAIN = "bluetooth_connection" +# arduino-pico's prebuilt BTstack is compiled with MAX_NR_GATT_CLIENTS 1; +# raising this needs an upstream change (the layer itself supports N). RP2_MAX_CONNECTIONS = 1 # Slot limits for the hub platforms running the connection-capable proxy; diff --git a/tests/component_tests/ble_client/test_validation.py b/tests/component_tests/ble_client/test_validation.py index 17e8c883a1..bb5c2bd71f 100644 --- a/tests/component_tests/ble_client/test_validation.py +++ b/tests/component_tests/ble_client/test_validation.py @@ -106,3 +106,6 @@ def test_legacy_node_choke_point_rejects_other_platforms() -> None: 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")) + # Through the public schema too, so removing the cv.All wiring fails here. + with pytest.raises(cv.Invalid, match="not been migrated"): + ble_client.BLE_CLIENT_SCHEMA({})