mirror of
https://github.com/esphome/esphome.git
synced 2026-09-30 16:30:22 +00:00
Enforce the hub surface with a concept
This commit is contained in:
@@ -86,8 +86,8 @@ struct HubCapabilities {
|
||||
bool scan_mode_switch;
|
||||
};
|
||||
|
||||
// The BLEHub method surface. Duck-typed: a tracker that misses one of these
|
||||
// fails to compile at the consumer's call site, not at a class definition.
|
||||
// The BLEHub method surface, enforced by the BLEHubContract concept below
|
||||
// (asserted where ble_hub_impl.h binds the alias).
|
||||
//
|
||||
// /// Register a parsed-advertisement consumer (BLE sensors, automation triggers).
|
||||
// void register_listener(ESPBTDeviceListener *listener);
|
||||
@@ -118,4 +118,18 @@ struct HubCapabilities {
|
||||
// /// consumers can gate features on the switch without probing.
|
||||
// bool request_scan_mode(bool active);
|
||||
|
||||
// set_scanner_state_callback is deliberately absent: it exists only on push
|
||||
// hubs under USE_BLE_SCANNER_STATE_CALLBACK.
|
||||
template<typename T>
|
||||
concept BLEHubContract = requires(T hub, ESPBTDeviceListener *listener, RawAdvertisementCallback raw_callback,
|
||||
uint8_t *mac) {
|
||||
hub.register_listener(listener);
|
||||
hub.set_raw_advertisement_callback(raw_callback);
|
||||
T::get_capabilities();
|
||||
hub.get_adapter_mac(mac);
|
||||
hub.scan_running();
|
||||
hub.scan_active();
|
||||
hub.request_scan_mode(true);
|
||||
};
|
||||
|
||||
} // namespace esphome::ble_device_base
|
||||
|
||||
@@ -6,28 +6,30 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ble_hub.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#if defined(USE_ESP32_BLE_TRACKER)
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#define ESPHOME_BLE_HUB_TYPE esp32_ble_tracker::ESP32BLETracker
|
||||
#elif defined(USE_RP2_BLE_TRACKER)
|
||||
#include "esphome/components/rp2_ble_tracker/rp2_ble_tracker.h"
|
||||
#define ESPHOME_BLE_HUB_TYPE rp2_ble_tracker::RP2BLETracker
|
||||
#elif defined(USE_BK72XX_BLE_TRACKER)
|
||||
#include "esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.h"
|
||||
#define ESPHOME_BLE_HUB_TYPE bk72xx_ble_tracker::BK72xxBLETracker
|
||||
#elif defined(USE_LN882H_BLE_TRACKER)
|
||||
#include "esphome/components/ln882h_ble_tracker/ln882h_ble_tracker.h"
|
||||
#define ESPHOME_BLE_HUB_TYPE ln882h_ble_tracker::LN882HBLETracker
|
||||
#endif
|
||||
// No #else on purpose: builds without a tracker (host unit tests) get no BLEHub.
|
||||
|
||||
namespace esphome::ble_device_base {
|
||||
|
||||
#if defined(USE_ESP32_BLE_TRACKER)
|
||||
using BLEHub = esp32_ble_tracker::ESP32BLETracker;
|
||||
#elif defined(USE_RP2_BLE_TRACKER)
|
||||
using BLEHub = rp2_ble_tracker::RP2BLETracker;
|
||||
#elif defined(USE_BK72XX_BLE_TRACKER)
|
||||
using BLEHub = bk72xx_ble_tracker::BK72xxBLETracker;
|
||||
#elif defined(USE_LN882H_BLE_TRACKER)
|
||||
using BLEHub = ln882h_ble_tracker::LN882HBLETracker;
|
||||
#ifdef ESPHOME_BLE_HUB_TYPE
|
||||
using BLEHub = ESPHOME_BLE_HUB_TYPE;
|
||||
static_assert(BLEHubContract<BLEHub>, "The build's BLE tracker is missing part of the BLEHub surface (ble_hub.h)");
|
||||
#undef ESPHOME_BLE_HUB_TYPE
|
||||
#endif
|
||||
|
||||
} // namespace esphome::ble_device_base
|
||||
|
||||
+12
-6
@@ -252,12 +252,12 @@
|
||||
// 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
|
||||
// std::array<uint64_t, N> instantiation a real build produces.
|
||||
// count (default 3) and the scanner-state push slot, _to_code_ble_hub() emits
|
||||
// the slot count (1 on rp2, 0 on advertisement-only hubs) — so static analysis
|
||||
// checks the same instantiations a real build produces.
|
||||
#ifdef USE_ESP32
|
||||
#define USE_BLE_SCANNER_STATE_CALLBACK
|
||||
#define BLUETOOTH_PROXY_MAX_CONNECTIONS 3
|
||||
#elif defined(USE_RP2)
|
||||
#define BLUETOOTH_PROXY_MAX_CONNECTIONS 1
|
||||
@@ -488,11 +488,17 @@
|
||||
|
||||
#ifdef USE_LIBRETINY
|
||||
#define USE_BK72XX_BLE
|
||||
#define USE_BK72XX_BLE_TRACKER
|
||||
#define BK72XX_BLE_SCAN_LISTENER_COUNT 1
|
||||
#define USE_LN882H_BLE
|
||||
#define USE_LN882H_BLE_TRACKER
|
||||
#define LN882H_BLE_SCAN_LISTENER_COUNT 1
|
||||
// One tracker arm per build: ln882x gets its real hub; bk72xx also stands in
|
||||
// for hub-less LibreTiny chips (rtl87xx) so bluetooth_proxy.h has a BLEHub
|
||||
// to parse against.
|
||||
#ifdef USE_LN882X
|
||||
#define USE_LN882H_BLE_TRACKER
|
||||
#else
|
||||
#define USE_BK72XX_BLE_TRACKER
|
||||
#endif
|
||||
#define ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT 1
|
||||
#define USE_CAPTIVE_PORTAL
|
||||
#define USE_WIFI_SCAN_RESULTS_LOCK
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "esphome/components/ble_device_base/ble_hub.h"
|
||||
|
||||
namespace esphome::ble_device_base::testing {
|
||||
|
||||
// Pins the request_scan_mode() contract documented in ble_hub.h: a hub
|
||||
// without a mode switch refuses without changing any state, a switching hub
|
||||
// honors and applies. Minimal host hubs mirror the in-tree tracker stubs.
|
||||
namespace {
|
||||
|
||||
class RefusingHub {
|
||||
public:
|
||||
static constexpr HubCapabilities get_capabilities() { return {false, false, false}; }
|
||||
bool scan_active() { return this->active_; }
|
||||
// The refusing shape every non-switching tracker provides: no state touched.
|
||||
bool request_scan_mode(bool active) { return false; }
|
||||
|
||||
protected:
|
||||
bool active_{true};
|
||||
};
|
||||
|
||||
class SwitchingHub : public RefusingHub {
|
||||
public:
|
||||
static constexpr HubCapabilities get_capabilities() { return {true, false, false, /* scan_mode_switch = */ true}; }
|
||||
bool request_scan_mode(bool active) {
|
||||
this->active_ = active;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// The esp32 shape: the controller supports active scanning but the hub keeps
|
||||
// the refusing stub (mode is driven through its own tracker API).
|
||||
class CapableRefusingHub : public RefusingHub {
|
||||
public:
|
||||
static constexpr HubCapabilities get_capabilities() { return {true, false, false}; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(BLEHubScanModeRequest, RefusingHubChangesNothing) {
|
||||
RefusingHub hub;
|
||||
EXPECT_TRUE(hub.scan_active());
|
||||
EXPECT_FALSE(hub.request_scan_mode(false));
|
||||
// Refused, not applied-and-reported-false: the state is untouched.
|
||||
EXPECT_TRUE(hub.scan_active());
|
||||
EXPECT_FALSE(hub.request_scan_mode(true));
|
||||
EXPECT_TRUE(hub.scan_active());
|
||||
}
|
||||
|
||||
TEST(BLEHubScanModeRequest, SwitchingHubHonorsAndApplies) {
|
||||
SwitchingHub hub;
|
||||
EXPECT_TRUE(hub.get_capabilities().scan_mode_switch);
|
||||
EXPECT_TRUE(hub.request_scan_mode(true));
|
||||
EXPECT_TRUE(hub.scan_active());
|
||||
EXPECT_TRUE(hub.request_scan_mode(false));
|
||||
EXPECT_FALSE(hub.scan_active());
|
||||
}
|
||||
|
||||
TEST(BLEHubScanModeRequest, CapabilityAndSwitchAreIndependent) {
|
||||
CapableRefusingHub hub;
|
||||
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_TRUE(hub.scan_active());
|
||||
}
|
||||
|
||||
} // namespace esphome::ble_device_base::testing
|
||||
Reference in New Issue
Block a user