Carry the address as uint64 in RawAdvertisement and unify the proxy setup

This commit is contained in:
J. Nick Koston
2026-08-08 00:12:50 -05:00
parent 5296539849
commit a6f81f3185
8 changed files with 32 additions and 67 deletions
@@ -159,7 +159,7 @@ void BK72xxBLETracker::dump_config() {
void BK72xxBLETracker::on_scan_report(const bk72xx_ble::BLEScanReport &report) {
// Raw callback (the raw-advertisement path).
if (this->raw_advertisement_callback_.is_set()) {
const ble_device_base::RawAdvertisement adv{.mac = report.mac,
const ble_device_base::RawAdvertisement adv{.address = ble_device_base::mac_lsb_first_to_uint64(report.mac),
.data = report.data,
.data_len = report.data_len,
.rssi = report.rssi,
+3 -2
View File
@@ -23,8 +23,9 @@ namespace esphome::ble_device_base {
/// One raw advertisement as delivered by the controller — a borrowed view,
/// valid only for the duration of the invoke() callback.
struct RawAdvertisement {
/// Least-significant octet first (BLE controller convention).
const uint8_t *mac;
/// Producers convert their native byte order at the emit site, so no
/// byte-order convention crosses this contract.
uint64_t address;
const uint8_t *data;
uint16_t data_len;
int8_t rssi; // signed dBm
@@ -26,18 +26,6 @@ BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; }
#ifdef USE_ESP32
void BluetoothProxy::setup() {
this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS;
this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS;
// Capture the configured scan mode from YAML before any API changes
this->configured_scan_active_ = this->parent_->get_scan_active();
this->hub_->set_raw_advertisement_callback({this, [](void *self, const ble_device_base::RawAdvertisement &adv) {
static_cast<BluetoothProxy *>(self)->on_raw_advertisement_(adv);
}});
}
void BluetoothProxy::on_scanner_state(esp32_ble_tracker::ScannerState state) {
if (this->api_connection_ != nullptr) {
this->send_bluetooth_scanner_state_(state);
@@ -47,8 +35,8 @@ void BluetoothProxy::on_scanner_state(esp32_ble_tracker::ScannerState state) {
void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state) {
api::BluetoothScannerStateResponse resp;
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
resp.mode = this->hub_->scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
resp.configured_mode = this->configured_scan_active_
? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
@@ -57,21 +45,6 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
#else // !USE_ESP32
void BluetoothProxy::setup() {
// BLUETOOTH_PROXY_MAX_CONNECTIONS is 0 on an advertisement-only proxy.
this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS;
this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS;
// Capture the configured scan mode from YAML before any API changes
this->configured_scan_active_ = this->hub_->scan_active();
// The hub delivers raw advertisements on the ESPHome main loop:
// mac is least-significant octet first (BLE controller convention).
this->hub_->set_raw_advertisement_callback({this, [](void *self, const ble_device_base::RawAdvertisement &adv) {
static_cast<BluetoothProxy *>(self)->on_raw_advertisement_(adv);
}});
}
void BluetoothProxy::send_bluetooth_scanner_state_() {
// One read feeds both the frame and the change detector; the detector only
// advances if the frame was accepted, so a dropped send (WOULD_BLOCK on a
@@ -92,15 +65,26 @@ void BluetoothProxy::send_bluetooth_scanner_state_() {
#endif // USE_ESP32
// The hub delivers raw advertisements on the ESPHome main loop; raw.mac is
// least-significant octet first (BLE controller convention).
void BluetoothProxy::setup() {
// BLUETOOTH_PROXY_MAX_CONNECTIONS is 0 on an advertisement-only proxy.
this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS;
this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS;
// Capture the configured scan mode from YAML before any API changes
this->configured_scan_active_ = this->hub_->scan_active();
this->hub_->set_raw_advertisement_callback({this, [](void *self, const ble_device_base::RawAdvertisement &adv) {
static_cast<BluetoothProxy *>(self)->on_raw_advertisement_(adv);
}});
}
// The hub delivers raw advertisements on the ESPHome main loop.
void BluetoothProxy::on_raw_advertisement_(const ble_device_base::RawAdvertisement &raw) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr)
return;
auto &adv = this->response_.advertisements[this->response_.advertisements_len];
// raw.mac is LSB-first; this matches ble_addr_to_uint64 on esp32.
adv.address = ble_device_base::mac_lsb_first_to_uint64(raw.mac);
adv.address = raw.address;
adv.rssi = raw.rssi;
adv.address_type = raw.addr_type;
uint8_t length = raw.data_len > sizeof(adv.data) ? sizeof(adv.data) : static_cast<uint8_t>(raw.data_len);
@@ -109,8 +93,7 @@ void BluetoothProxy::on_raw_advertisement_(const ble_device_base::RawAdvertiseme
this->response_.advertisements_len++;
ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", raw.mac[5], raw.mac[4],
raw.mac[3], raw.mac[2], raw.mac[1], raw.mac[0], length, raw.rssi);
ESP_LOGV(TAG, "Queuing raw packet from %012llX, length %d. RSSI: %d dB", raw.address, length, raw.rssi);
// Flush if we have reached BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE
if (this->response_.advertisements_len >= BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE) {
@@ -139,10 +122,6 @@ void BluetoothProxy::handle_gatt_not_connected_(uint64_t address, uint16_t handl
this->send_gatt_error(address, handle, GATT_NOT_CONNECTED);
}
#ifdef USE_ESP32
#endif // USE_ESP32
void BluetoothProxy::log_advertisement_flush_() {
ESP_LOGV(TAG, "Sent batch of %u BLE advertisements", this->response_.advertisements_len);
}
@@ -374,20 +374,6 @@ async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExp
return var
async def register_raw_ble_device(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
"""Register a BLE device listener that only needs raw advertisement data.
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used.
"""
_request_listener_slot()
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var))
return var
async def register_raw_client(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
@@ -464,12 +464,8 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) {
// Neutral raw-advertisement subscriber (the bluetooth_proxy path).
if (this->raw_advertisement_callback_.is_set()) {
uint8_t mac_lsb[6];
// bda is MSB-first; the neutral convention is LSB-first.
for (uint8_t i = 0; i < 6; i++)
mac_lsb[i] = scan_result.bda[5 - i];
ble_device_base::RawAdvertisement adv;
adv.mac = mac_lsb;
adv.address = esp32_ble::ble_addr_to_uint64(scan_result.bda);
adv.data = scan_result.ble_adv;
adv.data_len = static_cast<uint16_t>(scan_result.adv_data_len) + scan_result.scan_rsp_len;
adv.rssi = scan_result.rssi;
@@ -240,8 +240,11 @@ void LN882HBLETracker::process_adv_(const uint8_t *mac, int8_t rssi, uint8_t add
// Raw callback (the raw-advertisement path). Both full advertisements and
// unmatched scan responses (raw_only) are forwarded.
if (this->raw_advertisement_callback_.is_set()) {
const ble_device_base::RawAdvertisement adv{
.mac = mac, .data = data, .data_len = data_len, .rssi = rssi, .addr_type = addr_type};
const ble_device_base::RawAdvertisement adv{.address = ble_device_base::mac_lsb_first_to_uint64(mac),
.data = data,
.data_len = data_len,
.rssi = rssi,
.addr_type = addr_type};
this->raw_advertisement_callback_.invoke(adv);
}
@@ -122,7 +122,7 @@ void RP2BLETracker::dump_config() {
void RP2BLETracker::on_scan_report(const rp2040_ble::BLEScanReport &report) {
// Raw callback (the raw-advertisement path).
if (this->raw_advertisement_callback_.is_set()) {
const ble_device_base::RawAdvertisement adv{.mac = report.mac,
const ble_device_base::RawAdvertisement adv{.address = ble_device_base::mac_lsb_first_to_uint64(report.mac),
.data = report.data,
.data_len = report.data_len,
.rssi = report.rssi,
@@ -47,12 +47,12 @@ struct CapturingSubscriber {
};
// Device AA:BB:CC:DD:EE:FF — controller order delivers FF first.
const uint8_t MAC_LSB_FIRST[6] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa};
constexpr uint64_t TEST_ADDRESS = 0xAABBCCDDEEFFULL;
const uint8_t ADV_DATA[4] = {0x02, 0x01, 0x06, 0x00};
RawAdvertisement make_test_adv() {
return RawAdvertisement{
.mac = MAC_LSB_FIRST, .data = ADV_DATA, .data_len = sizeof(ADV_DATA), .rssi = -63, .addr_type = 1};
.address = TEST_ADDRESS, .data = ADV_DATA, .data_len = sizeof(ADV_DATA), .rssi = -63, .addr_type = 1};
}
} // namespace
@@ -70,7 +70,7 @@ TEST(RawAdvertisementCallback, SubscriberSeesFieldsUnchanged) {
hub.emit(make_test_adv());
ASSERT_EQ(subscriber.calls, 1);
EXPECT_EQ(subscriber.last.mac, MAC_LSB_FIRST);
EXPECT_EQ(subscriber.last.address, TEST_ADDRESS);
EXPECT_EQ(subscriber.last.data, ADV_DATA);
EXPECT_EQ(subscriber.last.data_len, sizeof(ADV_DATA));
EXPECT_EQ(subscriber.last.rssi, -63);