Merge branch 'esp32-proxy-hub-scanner-state' into esp32-tracker-parser-cleanup

This commit is contained in:
J. Nick Koston
2026-08-08 00:52:35 -05:00
6 changed files with 36 additions and 33 deletions
+14 -7
View File
@@ -61,8 +61,9 @@ enum class ScannerState : uint8_t {
};
/// Subscriber slot for scanner-state transitions; same shape as
/// RawAdvertisementCallback. Hubs that cannot push simply never invoke it and
/// the consumer falls back to polling scan_running().
/// RawAdvertisementCallback, delivered on the ESPHome main loop. Hubs that
/// cannot push drop the registration and the consumer falls back to polling
/// scan_running().
struct ScannerStateCallback {
void *instance{nullptr};
void (*fn)(void *instance, ScannerState state){nullptr};
@@ -99,10 +100,19 @@ class BLEHub {
/// Wire the raw-advertisement stream (bluetooth_proxy). One consumer at a time.
virtual void set_raw_advertisement_callback(RawAdvertisementCallback callback) = 0;
/// Push subscriber for scanner-state transitions (stored here; hubs invoke
/// scanner_state_callback_ where their state changes, if they can push).
#ifdef USE_BLE_SCANNER_STATE_CALLBACK
/// Push subscriber for scanner-state transitions; hubs that can push
/// invoke scanner_state_callback_ where their state changes. Compiled only
/// when a subscriber exists (bluetooth_proxy emits the define), so
/// subscriber-less builds carry no storage.
void set_scanner_state_callback(ScannerStateCallback callback) { this->scanner_state_callback_ = callback; }
protected:
ScannerStateCallback scanner_state_callback_{};
public:
#endif // USE_BLE_SCANNER_STATE_CALLBACK
virtual HubCapabilities get_capabilities() const = 0;
/// Adapter MAC in printable (MSB-first) order, out[0] = MSB.
@@ -122,9 +132,6 @@ class BLEHub {
/// HubCapabilities::scan_mode_switch, so consumers can gate features on the
/// switch without probing.
virtual bool request_scan_mode(bool active) { return false; }
protected:
ScannerStateCallback scanner_state_callback_{};
};
} // namespace esphome::ble_device_base
@@ -426,3 +426,5 @@ async def to_code(config: ConfigType) -> None:
cg.add_define("BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE", 16)
cg.add_define("USE_BLUETOOTH_PROXY")
# Compiles the scanner-state push slot into the hub (see ble_hub.h).
cg.add_define("USE_BLE_SCANNER_STATE_CALLBACK")
@@ -27,16 +27,20 @@ BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; }
// The neutral enum's values are the wire values.
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::IDLE) == api::enums::BLUETOOTH_SCANNER_STATE_IDLE);
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::STARTING) ==
api::enums::BLUETOOTH_SCANNER_STATE_STARTING);
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::RUNNING) ==
api::enums::BLUETOOTH_SCANNER_STATE_RUNNING);
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::FAILED) ==
api::enums::BLUETOOTH_SCANNER_STATE_FAILED);
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::STOPPING) ==
api::enums::BLUETOOTH_SCANNER_STATE_STOPPING);
static_assert(static_cast<uint32_t>(ble_device_base::ScannerState::STOPPED) ==
api::enums::BLUETOOTH_SCANNER_STATE_STOPPED);
void BluetoothProxy::on_scanner_state_(ble_device_base::ScannerState state) {
if (this->api_connection_ != nullptr) {
this->send_bluetooth_scanner_state_(state);
}
}
bool BluetoothProxy::send_bluetooth_scanner_state_(ble_device_base::ScannerState state) {
if (this->api_connection_ == nullptr)
return false;
api::BluetoothScannerStateResponse resp;
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
resp.mode = this->hub_->scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
@@ -72,7 +76,7 @@ void BluetoothProxy::setup() {
static_cast<BluetoothProxy *>(self)->on_raw_advertisement_(adv);
}});
this->hub_->set_scanner_state_callback({this, [](void *self, ble_device_base::ScannerState state) {
static_cast<BluetoothProxy *>(self)->on_scanner_state_(state);
static_cast<BluetoothProxy *>(self)->send_bluetooth_scanner_state_(state);
}});
}
@@ -503,7 +507,8 @@ void BluetoothProxy::loop() {
return;
}
// The hub has no scanner-state listener interface; poll and report on change.
// This hub doesn't push scanner-state transitions; poll and report on
// change. A hub gaining push must also refresh last_scan_running_ here.
if (this->hub_->scan_running() != this->last_scan_running_) {
this->send_polled_scanner_state_();
}
@@ -614,7 +619,7 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
}
this->api_connection_ = api_connection;
#ifdef USE_ESP32
this->send_bluetooth_scanner_state_(static_cast<ble_device_base::ScannerState>(this->parent_()->get_scanner_state()));
this->send_bluetooth_scanner_state_(this->parent_()->get_scanner_state());
#else
this->send_polled_scanner_state_();
#endif
@@ -412,16 +412,14 @@ void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
#endif
}
// The neutral enum mirrors this tracker's wire-aligned values.
static_assert(static_cast<int>(ScannerState::IDLE) == static_cast<int>(ble_device_base::ScannerState::IDLE));
static_assert(static_cast<int>(ScannerState::STOPPING) == static_cast<int>(ble_device_base::ScannerState::STOPPING));
void ESP32BLETracker::set_scanner_state_(ScannerState state) {
this->scanner_state_ = state;
this->state_version_++;
#ifdef USE_BLE_SCANNER_STATE_CALLBACK
if (this->scanner_state_callback_.is_set()) {
this->scanner_state_callback_.invoke(static_cast<ble_device_base::ScannerState>(state));
this->scanner_state_callback_.invoke(state);
}
#endif
#ifdef ESPHOME_ESP32_BLE_TRACKER_SCANNER_STATE_LISTENER_COUNT
for (auto *listener : this->scanner_state_listeners_) {
listener->on_scanner_state(state);
@@ -89,18 +89,8 @@ using ClientState = ble_device_base::ClientState;
using ConnectionType = ble_device_base::ConnectionType;
using ble_device_base::client_state_to_string;
enum class ScannerState {
// Scanner is idle, init state
IDLE,
// Scanner is starting
STARTING,
// Scanner is running
RUNNING,
// Scanner failed to start
FAILED,
// Scanner is stopping
STOPPING,
};
// Neutral scanner lifecycle re-exported for backward compatibility.
using ScannerState = ble_device_base::ScannerState;
/** Listener interface for BLE scanner state changes.
*
+1
View File
@@ -252,6 +252,7 @@
// platforms whose API/network types the proxy header cannot assume.
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_RP2)
#define USE_BLUETOOTH_PROXY
#define USE_BLE_SCANNER_STATE_CALLBACK
// Mirror the codegen values per platform: _to_code_esp32() emits the connection
// count (default 3), _to_code_ble_hub() emits the slot count (1 on rp2, 0 on
// advertisement-only hubs) — so static analysis checks the same