[ble_device_base] Advertise runtime scan-mode switching in HubCapabilities (#18079)

This commit is contained in:
Edvard Filistovič
2026-08-05 01:56:32 +00:00
committed by GitHub
parent ad18bbc644
commit 0a99b007a9
6 changed files with 22 additions and 8 deletions
@@ -91,7 +91,9 @@ class BK72xxBLETracker : public Component,
// controller never solicits scan responses and never merges them; consumers // controller never solicits scan responses and never merges them; consumers
// relying on scan-response fields (device names) get them only where the // relying on scan-response fields (device names) get them only where the
// receiver merges per address (Home Assistant does). No GATT client either. // receiver merges per address (Home Assistant does). No GATT client either.
return {.active_scan = false, .merges_scan_response = false, .gatt = false}; // scan_mode_switch stays false for the same reason: with no active-scan
// path there is no mode to switch to.
return {.active_scan = false, .merges_scan_response = false, .gatt = false, .scan_mode_switch = false};
} }
bool request_scan_mode(bool active) override { bool request_scan_mode(bool active) override {
// Passive-only controller: a passive request is already honored, an active // Passive-only controller: a passive request is already honored, an active
+8 -3
View File
@@ -59,6 +59,11 @@ struct HubCapabilities {
/// GATT client connections are available (today: esp32 only, but a chip SDK /// GATT client connections are available (today: esp32 only, but a chip SDK
/// gaining GATT support only has to flip this bit). /// gaining GATT support only has to flip this bit).
bool gatt; bool gatt;
/// request_scan_mode() is honored at runtime. Distinct from active_scan:
/// a passive-only controller (bk72xx) can never switch, and a hub may
/// support active scanning yet still refuse the runtime switch
/// (esp32_ble_tracker drives its mode through its own tracker API).
bool scan_mode_switch;
}; };
class BLEHub { class BLEHub {
@@ -86,9 +91,9 @@ class BLEHub {
/// picks it up on its next start. The default cannot-change keeps hubs /// picks it up on its next start. The default cannot-change keeps hubs
/// without a mode switch (and out-of-tree trackers) building unchanged. /// without a mode switch (and out-of-tree trackers) building unchanged.
/// Independent of HubCapabilities::active_scan: that bit describes what the /// Independent of HubCapabilities::active_scan: that bit describes what the
/// CONTROLLER can do, this method describes whether the hub exposes a /// CONTROLLER can do; whether this method honors requests is advertised by
/// runtime switch — a hub may support active scanning and still refuse /// HubCapabilities::scan_mode_switch, so consumers can gate features on the
/// (esp32_ble_tracker drives its mode through its own tracker API). /// switch without probing.
virtual bool request_scan_mode(bool active) { return false; } virtual bool request_scan_mode(bool active) { return false; }
}; };
@@ -242,7 +242,10 @@ class ESP32BLETracker final : public Component,
this->raw_advertisement_callback_ = callback; this->raw_advertisement_callback_ = callback;
} }
ble_device_base::HubCapabilities get_capabilities() const override { ble_device_base::HubCapabilities get_capabilities() const override {
return {/* active_scan = */ true, /* merges_scan_response = */ true, /* gatt = */ true}; // scan_mode_switch is false: the mode is driven through this tracker's own
// API (set_scan_active + restart), not the neutral request_scan_mode().
return {/* active_scan = */ true, /* merges_scan_response = */ true, /* gatt = */ true,
/* scan_mode_switch = */ false};
} }
void get_adapter_mac(uint8_t out[6]) override; void get_adapter_mac(uint8_t out[6]) override;
bool scan_running() override { return this->scanner_state_ == ScannerState::RUNNING; } bool scan_running() override { return this->scanner_state_ == ScannerState::RUNNING; }
@@ -72,7 +72,8 @@ class LN882HBLETracker : public Component,
// The LN882H controller supports active scanning; adv + scan response arrive // The LN882H controller supports active scanning; adv + scan response arrive
// as separate reports and are merged by this tracker (Bluedroid semantics). // as separate reports and are merged by this tracker (Bluedroid semantics).
// The SDK's GATT client is not exposed. // The SDK's GATT client is not exposed.
return {.active_scan = true, .merges_scan_response = true, .gatt = false}; // scan_mode_switch: request_scan_mode() is implemented (restart-if-running).
return {.active_scan = true, .merges_scan_response = true, .gatt = false, .scan_mode_switch = true};
} }
// The controller stores the address LSB-first (BLE convention); the contract // The controller stores the address LSB-first (BLE convention); the contract
// wants printable (MSB-first) order. // wants printable (MSB-first) order.
@@ -64,7 +64,7 @@ class RP2BLETracker : public Component,
// than merging them into the advertisement — consumers relying on // than merging them into the advertisement — consumers relying on
// scan-response fields (device names) get them only where the receiver // scan-response fields (device names) get them only where the receiver
// merges per address (Home Assistant does). No GATT path yet. // merges per address (Home Assistant does). No GATT path yet.
return {.active_scan = true, .merges_scan_response = false, .gatt = false}; return {.active_scan = true, .merges_scan_response = false, .gatt = false, .scan_mode_switch = true};
} }
// The controller stores the address in printable (MSB-first) order, which is // The controller stores the address in printable (MSB-first) order, which is
// exactly what the contract wants. // exactly what the contract wants.
@@ -29,7 +29,7 @@ class DefaultHub : public BLEHub {
class SwitchingHub : public DefaultHub { class SwitchingHub : public DefaultHub {
public: public:
HubCapabilities get_capabilities() const override { return {true, false, false}; } HubCapabilities get_capabilities() const override { return {true, false, false, /* scan_mode_switch = */ true}; }
bool request_scan_mode(bool active) override { bool request_scan_mode(bool active) override {
this->active_ = active; this->active_ = active;
return true; return true;
@@ -57,6 +57,7 @@ TEST(BLEHubScanModeRequest, DefaultRefusesAndChangesNothing) {
TEST(BLEHubScanModeRequest, OverrideHonorsAndApplies) { TEST(BLEHubScanModeRequest, OverrideHonorsAndApplies) {
SwitchingHub hub; SwitchingHub hub;
EXPECT_TRUE(hub.get_capabilities().scan_mode_switch);
EXPECT_TRUE(hub.request_scan_mode(true)); EXPECT_TRUE(hub.request_scan_mode(true));
EXPECT_TRUE(hub.scan_active()); EXPECT_TRUE(hub.scan_active());
EXPECT_TRUE(hub.request_scan_mode(false)); EXPECT_TRUE(hub.request_scan_mode(false));
@@ -66,6 +67,8 @@ TEST(BLEHubScanModeRequest, OverrideHonorsAndApplies) {
TEST(BLEHubScanModeRequest, CapabilityAndSwitchAreIndependent) { TEST(BLEHubScanModeRequest, CapabilityAndSwitchAreIndependent) {
CapableRefusingHub hub; CapableRefusingHub hub;
EXPECT_TRUE(hub.get_capabilities().active_scan); EXPECT_TRUE(hub.get_capabilities().active_scan);
// The esp32 shape advertises no runtime switch, and the request refuses.
EXPECT_FALSE(hub.get_capabilities().scan_mode_switch);
EXPECT_FALSE(hub.request_scan_mode(false)); EXPECT_FALSE(hub.request_scan_mode(false));
EXPECT_TRUE(hub.scan_active()); EXPECT_TRUE(hub.scan_active());
} }