Key backend and engine selection on their own defines

Each backend arm now has a codegen-emitted USE_BLE_GATT_BACKEND_<NAME>
define, making the alias ladder order-independent and letting the proxy
capability constants describe the backend instead of the platform (a second
esp32 backend carries its own facts). The legacy raw-gattc engine is
selected by USE_BLE_CLIENT_LEGACY_ENGINE instead of platform identity, so
esp32's future move to the neutral engine is one line per language. The
address-scoped maintenance calls key on the stack define they wrap.
This commit is contained in:
J. Nick Koston
2026-08-19 09:14:17 -05:00
parent 0a28a5b023
commit 27a97f36af
13 changed files with 61 additions and 34 deletions
+14 -5
View File
@@ -44,13 +44,20 @@ from esphome.types import ConfigType
# platforms' registries (the bluetooth_proxy pattern).
def _legacy_engine() -> bool:
"""True when the build uses the legacy raw-gattc engine - one line to
flip when esp32 moves to the neutral engine (with
USE_BLE_CLIENT_LEGACY_ENGINE in _to_code_esp32)."""
return CORE.is_esp32
def AUTO_LOAD() -> list[str]:
"""The engine's closure per platform: the legacy esp32 engine builds on
esp32_ble_client plus bluetooth_connection (the shared service-table
materializer; its sources compile empty in builds without a neutral
node), the neutral engine on the bluetooth_connection backend. The
platform-less arm is the union for manifest-resolving tooling."""
if CORE.is_esp32 or CORE.target_platform is None:
if _legacy_engine() or CORE.target_platform is None:
return ["bluetooth_connection", "esp32_ble_client"]
return ["bluetooth_connection"]
@@ -242,7 +249,7 @@ def _validate_platform(config: ConfigType) -> ConfigType:
# The language-schema dumper runs without a platform; expose the
# esp32 (legacy-engine) shape.
return _esp32_config_schema()
if CORE.is_esp32:
if _legacy_engine():
return _esp32_config_schema()(config)
if CORE.target_platform in bluetooth_connection.GATT_CLIENT_PLATFORMS:
return _gatt_config_schema(CORE.target_platform)(config)
@@ -269,7 +276,7 @@ class BLEClientFeatures(StrEnum):
def _engine_features() -> set[BLEClientFeatures]:
"""Features the validated platform's engine provides."""
if CORE.is_esp32:
if _legacy_engine():
return {
BLEClientFeatures.GATT_NODE,
BLEClientFeatures.RAW_GATTC,
@@ -342,10 +349,11 @@ def _request_gatt_node_build() -> None:
compiled in", plus the esp32 bridge/materializer defines."""
_request_node_slot()
cg.add_define("USE_BLE_CLIENT_GATT_NODES")
if CORE.is_esp32:
if _legacy_engine():
# Deliberately not ble_device_base.request_gatt_client(): that would
# claim a phantom backend slot on combined proxy builds.
cg.add_define("USE_BLE_GATT_CLIENT")
cg.add_define("USE_BLE_GATT_BACKEND_BLUEDROID")
cg.add_define("USE_BLE_GATT_SERVICE_TABLE")
@@ -525,6 +533,7 @@ async def _to_code_esp32(config: ConfigType) -> cg.MockObj:
# Register the loggers this component needs
esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP)
cg.add_define("USE_ESP32_BLE_UUID")
cg.add_define("USE_BLE_CLIENT_LEGACY_ENGINE")
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
@@ -552,7 +561,7 @@ async def _to_code_gatt(config: ConfigType) -> cg.MockObj:
async def to_code(config: ConfigType) -> None:
if CORE.is_esp32:
if _legacy_engine():
var = await _to_code_esp32(config)
else:
var = await _to_code_gatt(config)
+3 -1
View File
@@ -1,6 +1,8 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/core/defines.h"
#ifdef USE_BLE_CLIENT_LEGACY_ENGINE
#include "esphome/core/automation.h"
#include "esphome/components/ble_client/ble_client.h"
@@ -9,7 +9,7 @@
#include "esphome/core/defines.h"
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32)
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_BLE_CLIENT_LEGACY_ENGINE)
#include <tuple>
@@ -115,4 +115,4 @@ template<typename... Ts> class BLEClientDisconnectAction final : public Action<T
} // namespace esphome::ble_client
#endif // USE_BLE_GATT_CLIENT && !USE_ESP32
#endif // USE_BLE_GATT_CLIENT && !USE_BLE_CLIENT_LEGACY_ENGINE
+1 -1
View File
@@ -6,7 +6,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
#ifdef USE_BLE_CLIENT_LEGACY_ENGINE
#ifdef USE_BLE_CLIENT_GATT_NODES
#include "esphome/components/bluetooth_connection/bluetooth_connection.h"
+1 -1
View File
@@ -2,7 +2,7 @@
#include "esphome/core/defines.h"
#ifdef USE_ESP32
#ifdef USE_BLE_CLIENT_LEGACY_ENGINE
#include "ble_client_node.h"
#include "connect_backoff.h"
@@ -1,6 +1,6 @@
#include "ble_client_gatt.h"
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32)
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_BLE_CLIENT_LEGACY_ENGINE)
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
@@ -246,4 +246,4 @@ void BLEClient::dump_config() {
} // namespace esphome::ble_client
#endif // USE_BLE_GATT_CLIENT && !USE_ESP32
#endif // USE_BLE_GATT_CLIENT && !USE_BLE_CLIENT_LEGACY_ENGINE
@@ -13,7 +13,7 @@
#include "esphome/core/defines.h"
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_ESP32)
#if defined(USE_BLE_GATT_CLIENT) && !defined(USE_BLE_CLIENT_LEGACY_ENGINE)
#include "ble_client_node.h"
#include "connect_backoff.h"
@@ -146,4 +146,4 @@ class BLEClient : public Component,
} // namespace esphome::ble_client
#endif // USE_BLE_GATT_CLIENT && !USE_ESP32
#endif // USE_BLE_GATT_CLIENT && !USE_BLE_CLIENT_LEGACY_ENGINE
@@ -9,7 +9,7 @@
#ifdef USE_BLE_GATT_CLIENT
#include "esphome/components/ble_device_base/ble_gatt_client.h"
#endif
#ifdef USE_ESP32
#ifdef USE_BLE_CLIENT_LEGACY_ENGINE
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include <esp_gap_ble_api.h>
@@ -40,9 +40,9 @@ class BLEClientNode {
virtual void on_write_result(uint16_t handle, int error) {}
virtual void on_pairing_result(int status) {}
#endif
#ifdef USE_ESP32
// Legacy raw surface (esp32 engine only); components overriding these are
// esp32-only until migrated to the neutral surface above.
#ifdef USE_BLE_CLIENT_LEGACY_ENGINE
// Legacy raw surface; components overriding these need the legacy engine
// until migrated to the neutral surface above.
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {}
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {}
@@ -124,6 +124,8 @@ class _PlatformBackend:
backend_class: cg.MockObjClass
schema_fragment: Callable[[], cv.Schema]
register: Callable[[cg.MockObj, ConfigType], Awaitable[None]]
# Selects the backend's alias-ladder arm (order-independent arms).
define: str
# The single registry of platforms with a GATT client backend; a platform
@@ -131,9 +133,14 @@ class _PlatformBackend:
# platform's arm.
_PLATFORM_BACKENDS: dict[str, _PlatformBackend] = {
PLATFORM_ESP32: _PlatformBackend(
BluedroidGattClient, _esp32_schema_fragment, _esp32_register
BluedroidGattClient,
_esp32_schema_fragment,
_esp32_register,
"USE_BLE_GATT_BACKEND_BLUEDROID",
),
PLATFORM_RP2: _PlatformBackend(
RP2GattClient, _rp2_schema_fragment, _rp2_register, "USE_BLE_GATT_BACKEND_RP2"
),
PLATFORM_RP2: _PlatformBackend(RP2GattClient, _rp2_schema_fragment, _rp2_register),
}
# Gates dedicated-backend consumers (cv.only_on).
@@ -244,7 +251,9 @@ async def new_gatt_backend(
"""
from esphome.components import ble_device_base
entry = _backend_entry()
ble_device_base.request_gatt_client()
cg.add_define(entry.define)
if service_table:
cg.add_define("USE_BLE_GATT_SERVICE_TABLE")
backend = cg.new_Pvariable(config[CONF_BACKEND_ID])
@@ -253,7 +262,7 @@ async def new_gatt_backend(
# schema carries keys that register_component would misapply to the
# backend (e.g. a polling interval) must not put them in this config.
await cg.register_component(backend, config)
await _backend_entry().register(backend, config)
await entry.register(backend, config)
return backend
@@ -46,7 +46,7 @@ BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size
#endif // USE_BLUETOOTH_PROXY_CONNECTIONS
#if defined(USE_ESP32) && defined(USE_BLE_GATT_CLIENT)
#if defined(USE_ESP32_BLE) && defined(USE_BLE_GATT_CLIENT)
namespace esphome::bluetooth_connection {
// Address-scoped Bluedroid maintenance. Gated with the connection surface:
@@ -65,4 +65,4 @@ conn_err_t clear_gatt_cache(uint64_t address) {
}
} // namespace esphome::bluetooth_connection
#endif // USE_ESP32 && USE_BLE_GATT_CLIENT
#endif // USE_ESP32_BLE && USE_BLE_GATT_CLIENT
@@ -48,15 +48,16 @@ static constexpr conn_err_t CONN_OK = 0;
// GATT contract so backend and wrapper cannot drift.
static constexpr conn_err_t GATT_NOT_CONNECTED = ble_device_base::GATT_ERR_NOT_CONNECTED;
// What the platform's connection backend supports beyond GATT operations;
// the proxy derives its feature flags and legacy version from these.
#if defined(USE_ESP32)
// What the build's connection backend supports beyond GATT operations; the
// proxy derives its feature flags and legacy version from these. Keyed on
// the backend define, never the platform, so a second backend on one
// platform carries its own facts.
#if defined(USE_BLE_GATT_BACKEND_BLUEDROID)
static constexpr bool SUPPORTS_PAIRING = true;
static constexpr bool SUPPORTS_CACHE_CLEARING = true;
#elif defined(USE_RP2040_BLE) && defined(USE_BLE_GATT_CLIENT)
#elif defined(USE_BLE_GATT_BACKEND_RP2)
// The rp2 BTstack backend pairs (just works + bonding); it has no service
// cache to clear. Keyed on the backend, not the generic client define, so a
// future backend without pairing keeps the stub arm below.
// cache to clear.
static constexpr bool SUPPORTS_PAIRING = true;
static constexpr bool SUPPORTS_CACHE_CLEARING = false;
#else
@@ -64,13 +65,14 @@ static constexpr bool SUPPORTS_PAIRING = false;
static constexpr bool SUPPORTS_CACHE_CLEARING = false;
#endif
// Address-scoped (not connection-scoped) maintenance requests.
#if (defined(USE_ESP32) || defined(USE_RP2040_BLE)) && defined(USE_BLE_GATT_CLIENT)
// Address-scoped (not connection-scoped) maintenance requests; keyed on the
// stack (the calls need no backend instance).
#if (defined(USE_ESP32_BLE) || defined(USE_RP2040_BLE)) && defined(USE_BLE_GATT_CLIENT)
conn_err_t unpair_device(uint64_t address);
#else
inline conn_err_t unpair_device(uint64_t) { return GATT_NOT_CONNECTED; }
#endif
#if defined(USE_ESP32) && defined(USE_BLE_GATT_CLIENT)
#if defined(USE_ESP32_BLE) && defined(USE_BLE_GATT_CLIENT)
conn_err_t clear_gatt_cache(uint64_t address);
#else
inline conn_err_t clear_gatt_cache(uint64_t) { return GATT_NOT_CONNECTED; }
@@ -12,10 +12,12 @@
#include "esphome/components/ble_device_base/ble_gatt_client.h"
#if defined(USE_RP2040_BLE)
// Arms are keyed on codegen-emitted per-backend defines (_PLATFORM_BACKENDS
// in __init__.py), so they are order-independent.
#if defined(USE_BLE_GATT_BACKEND_RP2)
#include "bluetooth_connection_rp2.h"
#define ESPHOME_BLE_GATT_CONNECTION_TYPE bluetooth_connection::RP2GattClient
#elif defined(USE_ESP32_BLE)
#elif defined(USE_BLE_GATT_BACKEND_BLUEDROID)
#include "bluetooth_connection_bluedroid.h"
#define ESPHOME_BLE_GATT_CONNECTION_TYPE bluetooth_connection::BluedroidGattClient
#elif defined(USE_BLE_GATT_CLIENT_STUB_BACKEND)
+3
View File
@@ -318,8 +318,10 @@
#define USE_ESP32_BLE_SERVER_ON_DISCONNECT
#define USE_ESP32_BLE_TRACKER
#define USE_BLE_GATT_CLIENT
#define USE_BLE_GATT_BACKEND_BLUEDROID
#define USE_BLE_GATT_SERVICE_TABLE
#define USE_BLE_CLIENT_GATT_NODES
#define USE_BLE_CLIENT_LEGACY_ENGINE
#define ESPHOME_BLE_CLIENT_MAX_NODES 1
#define ESPHOME_BLE_GATT_CLIENT_COUNT 1
#define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1
@@ -488,6 +490,7 @@
#define ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT 1
#define USE_BLE_SCAN_RESPONSE_MERGER
#define USE_BLE_GATT_CLIENT
#define USE_BLE_GATT_BACKEND_RP2
#define USE_BLE_CLIENT_GATT_NODES
#define ESPHOME_BLE_GATT_CLIENT_COUNT 3
#define ESPHOME_BLE_CLIENT_MAX_NODES 1