mirror of
https://github.com/esphome/esphome.git
synced 2026-10-10 13:33:06 +00:00
Apply the simplify findings and fix the passive esp32 build
This commit is contained in:
@@ -16,10 +16,11 @@
|
||||
#include <esp_err.h>
|
||||
#endif
|
||||
|
||||
// A GATT connection backend exists in this build: esp32 (Bluedroid) or a hub
|
||||
// platform with the neutral GATT client compiled in. Single-sourced here so
|
||||
// the proxy and this component cannot drift.
|
||||
#if defined(USE_ESP32) || defined(USE_BLE_GATT_CLIENT)
|
||||
// The connection-aware API request handlers are compiled: a GATT backend is
|
||||
// wired by codegen (one slot per connection). Advertisement-only builds get
|
||||
// the clean-error handlers; address-scoped maintenance (unpair, cache clear)
|
||||
// still works there through the per-platform free functions below.
|
||||
#ifdef USE_BLE_GATT_CLIENT
|
||||
#define BLUETOOTH_CONNECTION_HAS_GATT
|
||||
#endif
|
||||
|
||||
|
||||
@@ -246,9 +246,7 @@ void BluedroidGattClient::set_disconnecting_() {
|
||||
}
|
||||
|
||||
void BluedroidGattClient::report_connection_state_(bool connected, int error) {
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_connection_state(connected, this->mtu_, error);
|
||||
}
|
||||
this->listener_->on_connection_state(connected, this->mtu_, error);
|
||||
}
|
||||
|
||||
esp_err_t BluedroidGattClient::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency,
|
||||
@@ -286,9 +284,7 @@ void BluedroidGattClient::handle_search_cmpl_() {
|
||||
esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_SECONDARY_SERVICE, 0x0001, 0xFFFF, 0,
|
||||
&secondary);
|
||||
this->service_total_ = primary + secondary;
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_service_discovery_done(0);
|
||||
}
|
||||
this->listener_->on_service_discovery_done(0);
|
||||
}
|
||||
|
||||
void BluedroidGattClient::stream_service_batch(BluetoothConnection &conn) {
|
||||
@@ -554,46 +550,35 @@ bool BluedroidGattClient::handle_gattc_event_(esp_gattc_cb_event_t event, esp_ga
|
||||
case ESP_GATTC_READ_DESCR_EVT: {
|
||||
if (this->conn_id_ != param->read.conn_id)
|
||||
return false;
|
||||
if (this->listener_ != nullptr) {
|
||||
bool ok = param->read.status == ESP_GATT_OK;
|
||||
this->listener_->on_read_result(param->read.handle, ok ? param->read.value : nullptr,
|
||||
ok ? param->read.value_len : 0, ok ? 0 : param->read.status);
|
||||
}
|
||||
bool ok = param->read.status == ESP_GATT_OK;
|
||||
this->listener_->on_read_result(param->read.handle, ok ? param->read.value : nullptr,
|
||||
ok ? param->read.value_len : 0, ok ? 0 : param->read.status);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||
case ESP_GATTC_WRITE_DESCR_EVT: {
|
||||
if (this->conn_id_ != param->write.conn_id)
|
||||
return false;
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_write_result(param->write.handle,
|
||||
param->write.status == ESP_GATT_OK ? 0 : param->write.status);
|
||||
}
|
||||
this->listener_->on_write_result(param->write.handle,
|
||||
param->write.status == ESP_GATT_OK ? 0 : param->write.status);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_notify_state(
|
||||
param->reg_for_notify.handle, true,
|
||||
param->reg_for_notify.status == ESP_GATT_OK ? 0 : param->reg_for_notify.status);
|
||||
}
|
||||
this->listener_->on_notify_state(param->reg_for_notify.handle, true,
|
||||
param->reg_for_notify.status == ESP_GATT_OK ? 0 : param->reg_for_notify.status);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_notify_state(
|
||||
param->unreg_for_notify.handle, false,
|
||||
param->unreg_for_notify.status == ESP_GATT_OK ? 0 : param->unreg_for_notify.status);
|
||||
}
|
||||
this->listener_->on_notify_state(
|
||||
param->unreg_for_notify.handle, false,
|
||||
param->unreg_for_notify.status == ESP_GATT_OK ? 0 : param->unreg_for_notify.status);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (this->conn_id_ != param->notify.conn_id)
|
||||
return false;
|
||||
ESP_LOGV(TAG, "[%d] NOTIFY_EVT handle=0x%2X", this->connection_index_, param->notify.handle);
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_notify_data(param->notify.handle, param->notify.value, param->notify.value_len);
|
||||
}
|
||||
this->listener_->on_notify_data(param->notify.handle, param->notify.value, param->notify.value_len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -614,10 +599,8 @@ void BluedroidGattClient::handle_gap_event_(esp_gap_ble_cb_event_t event, esp_bl
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
|
||||
if (!this->check_addr_(param->ble_security.auth_cmpl.bd_addr))
|
||||
break;
|
||||
if (this->listener_ != nullptr) {
|
||||
this->listener_->on_pairing_result(
|
||||
param->ble_security.auth_cmpl.success ? 0 : param->ble_security.auth_cmpl.fail_reason);
|
||||
}
|
||||
this->listener_->on_pairing_result(
|
||||
param->ble_security.auth_cmpl.success ? 0 : param->ble_security.auth_cmpl.fail_reason);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -52,6 +52,7 @@ class BluedroidGattClient final : public Component {
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_BLUETOOTH; }
|
||||
|
||||
// Wired by codegen before setup and invariant for the device lifetime.
|
||||
void set_listener(BluetoothConnection *listener) { this->listener_ = listener; }
|
||||
esp32_ble_tracker::ESPBTClient *tracker_client() { return &this->shim_; }
|
||||
|
||||
|
||||
@@ -12,23 +12,15 @@
|
||||
|
||||
namespace esphome::bluetooth_connection {
|
||||
|
||||
namespace {
|
||||
void address_to_bda(uint64_t address, esp_bd_addr_t &bda) {
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
bda[i] = (address >> ((5 - i) * 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
conn_err_t unpair_device(uint64_t address) {
|
||||
esp_bd_addr_t bda;
|
||||
address_to_bda(address, bda);
|
||||
ble_device_base::uint64_to_mac_msb_first(address, bda);
|
||||
return esp_ble_remove_bond_device(bda);
|
||||
}
|
||||
|
||||
conn_err_t clear_gatt_cache(uint64_t address) {
|
||||
esp_bd_addr_t bda;
|
||||
address_to_bda(address, bda);
|
||||
ble_device_base::uint64_to_mac_msb_first(address, bda);
|
||||
esp_ble_gattc_cache_clean(bda);
|
||||
return CONN_OK;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ class StubGattBackend {
|
||||
return ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
}
|
||||
ble_device_base::GattServiceTable get_service_table() { return {}; }
|
||||
bool disconnect_pending() const { return false; }
|
||||
void cancel_pending_disconnect() {}
|
||||
void set_connection_type(ble_device_base::ConnectionType ct) {}
|
||||
void release_services() {}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Hub-platform BluetoothConnection: drives the build's GATT backend (the
|
||||
// BluetoothConnection: drives the build's GATT backend (the
|
||||
// ble_device_base::BLEGattConnection alias) and translates its events into
|
||||
// the same API messages the esp32 class emits.
|
||||
// Presents the identical method surface, so the proxy's GATT dispatch
|
||||
// compiles against either class unchanged.
|
||||
// the proxy's API messages. One wrapper for every platform; per-backend
|
||||
// differences live behind the alias and the streamer cut-through.
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -52,11 +51,8 @@ class BluetoothConnection final {
|
||||
bool is_paired() const { return this->paired_; }
|
||||
void set_unpaired() { this->paired_ = false; }
|
||||
conn_err_t pair() { return this->backend_->pair(); }
|
||||
// Backends with deferred-disconnect state (bluedroid) answer through the
|
||||
// detected forwards; the rest have nothing to track. Templates so the
|
||||
// discarded branch is not odr-checked against backends without the methods.
|
||||
bool disconnect_pending() const { return disconnect_pending_(this->backend_); }
|
||||
void cancel_pending_disconnect() { cancel_pending_(this->backend_); }
|
||||
bool disconnect_pending() const { return this->backend_->disconnect_pending(); }
|
||||
void cancel_pending_disconnect() { this->backend_->cancel_pending_disconnect(); }
|
||||
|
||||
void set_address(uint64_t address);
|
||||
uint64_t get_address() const { return this->address_; }
|
||||
@@ -69,8 +65,8 @@ class BluetoothConnection final {
|
||||
void set_connection_type(ConnectionType ct) {
|
||||
this->connection_type_ = ct;
|
||||
// The bluedroid backend branches on the type itself (prefer-params and
|
||||
// the with-cache report at OPEN_EVT).
|
||||
forward_connection_type_(this->backend_, ct);
|
||||
// the with-cache report at OPEN_EVT); the others ignore it.
|
||||
this->backend_->set_connection_type(ct);
|
||||
}
|
||||
// Latched at discovery completion rather than read from the backend table:
|
||||
// streaming frees the table, and this must stay true for the connection's
|
||||
@@ -79,14 +75,17 @@ class BluetoothConnection final {
|
||||
bool has_gatt_services() const { return this->services_discovered_; }
|
||||
|
||||
/// Stream any pending service-discovery batch and police the disconnect
|
||||
/// safety timeout. Called from the proxy's loop — hub connections have no
|
||||
/// Component loop of their own (the esp32 class streams from its own
|
||||
/// loop() and has the same 10 s safety net in its base class).
|
||||
/// safety timeout. Called from the proxy's loop — the wrapper has no
|
||||
/// Component loop of its own.
|
||||
void process_pending_services() {
|
||||
if (this->send_service_ >= 0) {
|
||||
this->stream_pending_(this->backend_);
|
||||
}
|
||||
this->check_disconnect_timeout_();
|
||||
// Inline state gate: this runs per loop iteration for every slot, and the
|
||||
// 10 s safety net only matters while DISCONNECTING.
|
||||
if (this->state_ == ClientState::DISCONNECTING) {
|
||||
this->check_disconnect_timeout_();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- backend event sink (called directly by the backend, main loop) ----
|
||||
@@ -108,23 +107,6 @@ class BluetoothConnection final {
|
||||
// response in place from its stack cache; the rest use the table streamer.
|
||||
// Template so the discarded branch is not odr-checked against backends
|
||||
// that lack the method.
|
||||
template<typename Backend> static bool disconnect_pending_(Backend *backend) {
|
||||
if constexpr (requires { backend->disconnect_pending(); }) {
|
||||
return backend->disconnect_pending();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
template<typename Backend> static void cancel_pending_(Backend *backend) {
|
||||
if constexpr (requires { backend->cancel_pending_disconnect(); }) {
|
||||
backend->cancel_pending_disconnect();
|
||||
}
|
||||
}
|
||||
template<typename Backend> static void forward_connection_type_(Backend *backend, ConnectionType ct) {
|
||||
if constexpr (requires { backend->set_connection_type(ct); }) {
|
||||
backend->set_connection_type(ct);
|
||||
}
|
||||
}
|
||||
template<typename Backend> void stream_pending_(Backend *backend) {
|
||||
if constexpr (requires { backend->stream_service_batch(*this); }) {
|
||||
backend->stream_service_batch(*this);
|
||||
|
||||
@@ -94,6 +94,11 @@ class RP2GattClient final : public Component, public Parented<rp2040_ble::RP2040
|
||||
int pair();
|
||||
int update_connection_params(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout);
|
||||
ble_device_base::GattServiceTable get_service_table();
|
||||
// No deferred-disconnect state (disconnect is one call) and no
|
||||
// connection-type branching on this backend.
|
||||
bool disconnect_pending() const { return false; }
|
||||
void cancel_pending_disconnect() {}
|
||||
void set_connection_type(ble_device_base::ConnectionType ct) {}
|
||||
void release_services();
|
||||
|
||||
protected:
|
||||
|
||||
@@ -213,20 +213,31 @@ def _rp2_config_schema() -> cv.All:
|
||||
return cv.All(schema, populate_connections)
|
||||
|
||||
|
||||
async def _rp2_connections_to_code(var: cg.MockObj, config: ConfigType) -> None:
|
||||
from esphome.components import rp2040_ble
|
||||
|
||||
# One wrapper + backend pair per slot (the esp32 arm's pattern).
|
||||
for connection_conf in config[CONF_CONNECTIONS]:
|
||||
async def _connections_to_code(
|
||||
var: cg.MockObj, config: ConfigType, register_backend
|
||||
) -> None:
|
||||
"""One wrapper + backend pair per slot; register_backend supplies the
|
||||
platform's backend registration (tracker client on esp32, controller
|
||||
parent on rp2)."""
|
||||
for connection_conf in config.get(CONF_CONNECTIONS, []):
|
||||
ble_device_base.request_gatt_client()
|
||||
backend = cg.new_Pvariable(connection_conf[CONF_BACKEND_ID])
|
||||
await cg.register_component(backend, connection_conf)
|
||||
await cg.register_parented(backend, config[rp2040_ble.CONF_RP2040_BLE_ID])
|
||||
await register_backend(backend, connection_conf, config)
|
||||
connection = cg.new_Pvariable(connection_conf[CONF_ID])
|
||||
cg.add(connection.set_backend(backend))
|
||||
cg.add(var.register_connection(connection))
|
||||
|
||||
|
||||
async def _rp2_connections_to_code(var: cg.MockObj, config: ConfigType) -> None:
|
||||
from esphome.components import rp2040_ble
|
||||
|
||||
async def register_backend(backend, connection_conf, config):
|
||||
await cg.register_parented(backend, config[rp2040_ble.CONF_RP2040_BLE_ID])
|
||||
|
||||
await _connections_to_code(var, config, register_backend)
|
||||
|
||||
|
||||
# Per-platform schema builders and connection codegen; every key of
|
||||
# bluetooth_connection.HUB_MAX_CONNECTIONS needs an entry in both (pinned by
|
||||
# tests/component_tests/bluetooth_proxy/).
|
||||
@@ -386,17 +397,13 @@ async def _to_code_esp32(config: ConfigType) -> None:
|
||||
connection_count = len(config.get(CONF_CONNECTIONS, []))
|
||||
cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", connection_count)
|
||||
|
||||
for connection_conf in config.get(CONF_CONNECTIONS, []):
|
||||
ble_device_base.request_gatt_client()
|
||||
backend = cg.new_Pvariable(connection_conf[CONF_BACKEND_ID])
|
||||
await cg.register_component(backend, connection_conf)
|
||||
async def register_backend(backend, connection_conf, _config):
|
||||
# The tracker promote loop drives connect timing through the shim.
|
||||
await esp32_ble_tracker.register_raw_client(
|
||||
backend.tracker_client(), connection_conf
|
||||
)
|
||||
connection = cg.new_Pvariable(connection_conf[CONF_ID])
|
||||
cg.add(connection.set_backend(backend))
|
||||
cg.add(var.register_connection(connection))
|
||||
|
||||
await _connections_to_code(var, config, register_backend)
|
||||
|
||||
if config.get(CONF_CACHE_SERVICES):
|
||||
add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)
|
||||
|
||||
@@ -477,9 +477,8 @@ void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {
|
||||
|
||||
void BluetoothProxy::loop() {
|
||||
#ifdef BLUETOOTH_CONNECTION_HAS_GATT
|
||||
// Stream pending service-discovery batches every iteration (esp32 parity:
|
||||
// its connections stream from their own per-iteration Component loop).
|
||||
// send_service_for_discovery_() handles a vanished API connection itself.
|
||||
// Stream pending service-discovery batches every iteration; the streamer
|
||||
// handles a vanished API connection itself.
|
||||
for (uint8_t i = 0; i < this->connection_count_; i++) {
|
||||
this->connections_[i]->process_pending_services();
|
||||
}
|
||||
@@ -539,12 +538,18 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_PAIR:
|
||||
this->send_device_pairing(msg.address, false, GATT_NOT_CONNECTED);
|
||||
break;
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR:
|
||||
this->send_device_unpairing(msg.address, false, GATT_NOT_CONNECTED);
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR: {
|
||||
// Address-scoped maintenance needs no connection slot: real on esp32
|
||||
// (Bluedroid bond table), the stub elsewhere keeps the old error reply.
|
||||
conn_err_t ret = bluetooth_connection::unpair_device(msg.address);
|
||||
this->send_device_unpairing(msg.address, ret == CONN_OK, ret);
|
||||
break;
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE:
|
||||
this->send_device_clear_cache(msg.address, false, GATT_NOT_CONNECTED);
|
||||
}
|
||||
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: {
|
||||
conn_err_t ret = bluetooth_connection::clear_gatt_cache(msg.address);
|
||||
this->send_device_clear_cache(msg.address, ret == CONN_OK, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/api/api_connection.h"
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
@@ -27,7 +25,6 @@ namespace esphome::bluetooth_proxy {
|
||||
// re-exported here so the proxy code reads unqualified.
|
||||
using bluetooth_connection::CONN_OK;
|
||||
using bluetooth_connection::conn_err_t;
|
||||
using bluetooth_connection::DONE_SENDING_SERVICES;
|
||||
using bluetooth_connection::GATT_NOT_CONNECTED;
|
||||
using bluetooth_connection::INIT_SENDING_SERVICES;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Advertisement-only proxy on esp32 by explicit choice: no GATT backend is
|
||||
# compiled (USE_BLE_GATT_CLIENT unset), which pins the HAS_GATT gating and the
|
||||
# address-scoped maintenance path that a connections build never exercises.
|
||||
packages:
|
||||
common: !include common.yaml
|
||||
|
||||
esp32_ble_tracker:
|
||||
|
||||
bluetooth_proxy:
|
||||
active: false
|
||||
Reference in New Issue
Block a user