ESPHome  2024.12.2
bluetooth_proxy.cpp
Go to the documentation of this file.
1 #include "bluetooth_proxy.h"
2 
3 #include "esphome/core/log.h"
4 #include "esphome/core/macros.h"
5 
6 #ifdef USE_ESP32
7 
8 namespace esphome {
9 namespace bluetooth_proxy {
10 
11 static const char *const TAG = "bluetooth_proxy";
12 static const int DONE_SENDING_SERVICES = -2;
13 
14 std::vector<uint64_t> get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) {
15  esp_bt_uuid_t uuid = espbt::ESPBTUUID::from_uuid(uuid_source).as_128bit().get_uuid();
16  return std::vector<uint64_t>{((uint64_t) uuid.uuid.uuid128[15] << 56) | ((uint64_t) uuid.uuid.uuid128[14] << 48) |
17  ((uint64_t) uuid.uuid.uuid128[13] << 40) | ((uint64_t) uuid.uuid.uuid128[12] << 32) |
18  ((uint64_t) uuid.uuid.uuid128[11] << 24) | ((uint64_t) uuid.uuid.uuid128[10] << 16) |
19  ((uint64_t) uuid.uuid.uuid128[9] << 8) | ((uint64_t) uuid.uuid.uuid128[8]),
20  ((uint64_t) uuid.uuid.uuid128[7] << 56) | ((uint64_t) uuid.uuid.uuid128[6] << 48) |
21  ((uint64_t) uuid.uuid.uuid128[5] << 40) | ((uint64_t) uuid.uuid.uuid128[4] << 32) |
22  ((uint64_t) uuid.uuid.uuid128[3] << 24) | ((uint64_t) uuid.uuid.uuid128[2] << 16) |
23  ((uint64_t) uuid.uuid.uuid128[1] << 8) | ((uint64_t) uuid.uuid.uuid128[0])};
24 }
25 
27 
29  if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_)
30  return false;
31 
32  ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
33  device.get_rssi());
34  this->send_api_packet_(device);
35  return true;
36 }
37 
38 bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
39  if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
40  return false;
41 
43  for (size_t i = 0; i < count; i++) {
44  auto &result = advertisements[i];
46  adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
47  adv.rssi = result.rssi;
48  adv.address_type = result.ble_addr_type;
49 
50  uint8_t length = result.adv_data_len + result.scan_rsp_len;
51  adv.data.reserve(length);
52  for (uint16_t i = 0; i < length; i++) {
53  adv.data.push_back(result.ble_adv[i]);
54  }
55 
56  resp.advertisements.push_back(std::move(adv));
57 
58  ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
59  result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
60  }
61  ESP_LOGV(TAG, "Proxying %d packets", count);
63  return true;
64 }
67  resp.address = device.address_uint64();
68  resp.address_type = device.get_address_type();
69  if (!device.get_name().empty())
70  resp.name = device.get_name();
71  resp.rssi = device.get_rssi();
72  for (auto uuid : device.get_service_uuids()) {
73  resp.service_uuids.push_back(uuid.to_string());
74  }
75  for (auto &data : device.get_service_datas()) {
76  api::BluetoothServiceData service_data;
77  service_data.uuid = data.uuid.to_string();
78  service_data.data.assign(data.data.begin(), data.data.end());
79  resp.service_data.push_back(std::move(service_data));
80  }
81  for (auto &data : device.get_manufacturer_datas()) {
82  api::BluetoothServiceData manufacturer_data;
83  manufacturer_data.uuid = data.uuid.to_string();
84  manufacturer_data.data.assign(data.data.begin(), data.data.end());
85  resp.manufacturer_data.push_back(std::move(manufacturer_data));
86  }
88 }
89 
91  ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
92  ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_));
93  ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size());
94  ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_));
95 }
96 
98  int free = 0;
99  for (auto *connection : this->connections_) {
100  if (connection->address_ == 0) {
101  free++;
102  ESP_LOGV(TAG, "[%d] Free connection", connection->get_connection_index());
103  } else {
104  ESP_LOGV(TAG, "[%d] Used connection by [%s]", connection->get_connection_index(),
105  connection->address_str().c_str());
106  }
107  }
108  return free;
109 }
110 
112  if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr) {
113  for (auto *connection : this->connections_) {
114  if (connection->get_address() != 0) {
115  connection->disconnect();
116  }
117  }
118  return;
119  }
120  for (auto *connection : this->connections_) {
121  if (connection->send_service_ == connection->service_count_) {
122  connection->send_service_ = DONE_SENDING_SERVICES;
123  this->send_gatt_services_done(connection->get_address());
124  if (connection->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
125  connection->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
126  connection->release_services();
127  }
128  } else if (connection->send_service_ >= 0) {
129  esp_gattc_service_elem_t service_result;
130  uint16_t service_count = 1;
131  esp_gatt_status_t service_status =
132  esp_ble_gattc_get_service(connection->get_gattc_if(), connection->get_conn_id(), nullptr, &service_result,
133  &service_count, connection->send_service_);
134  connection->send_service_++;
135  if (service_status != ESP_GATT_OK) {
136  ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service error at offset=%d, status=%d",
137  connection->get_connection_index(), connection->address_str().c_str(), connection->send_service_ - 1,
138  service_status);
139  continue;
140  }
141  if (service_count == 0) {
142  ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service missing, service_count=%d",
143  connection->get_connection_index(), connection->address_str().c_str(), service_count);
144  continue;
145  }
147  resp.address = connection->get_address();
148  api::BluetoothGATTService service_resp;
149  service_resp.uuid = get_128bit_uuid_vec(service_result.uuid);
150  service_resp.handle = service_result.start_handle;
151  uint16_t char_offset = 0;
152  esp_gattc_char_elem_t char_result;
153  while (true) { // characteristics
154  uint16_t char_count = 1;
155  esp_gatt_status_t char_status = esp_ble_gattc_get_all_char(
156  connection->get_gattc_if(), connection->get_conn_id(), service_result.start_handle,
157  service_result.end_handle, &char_result, &char_count, char_offset);
158  if (char_status == ESP_GATT_INVALID_OFFSET || char_status == ESP_GATT_NOT_FOUND) {
159  break;
160  }
161  if (char_status != ESP_GATT_OK) {
162  ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", connection->get_connection_index(),
163  connection->address_str().c_str(), char_status);
164  break;
165  }
166  if (char_count == 0) {
167  break;
168  }
169  api::BluetoothGATTCharacteristic characteristic_resp;
170  characteristic_resp.uuid = get_128bit_uuid_vec(char_result.uuid);
171  characteristic_resp.handle = char_result.char_handle;
172  characteristic_resp.properties = char_result.properties;
173  char_offset++;
174  uint16_t desc_offset = 0;
175  esp_gattc_descr_elem_t desc_result;
176  while (true) { // descriptors
177  uint16_t desc_count = 1;
178  esp_gatt_status_t desc_status =
179  esp_ble_gattc_get_all_descr(connection->get_gattc_if(), connection->get_conn_id(),
180  char_result.char_handle, &desc_result, &desc_count, desc_offset);
181  if (desc_status == ESP_GATT_INVALID_OFFSET || desc_status == ESP_GATT_NOT_FOUND) {
182  break;
183  }
184  if (desc_status != ESP_GATT_OK) {
185  ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_all_descr error, status=%d", connection->get_connection_index(),
186  connection->address_str().c_str(), desc_status);
187  break;
188  }
189  if (desc_count == 0) {
190  break;
191  }
192  api::BluetoothGATTDescriptor descriptor_resp;
193  descriptor_resp.uuid = get_128bit_uuid_vec(desc_result.uuid);
194  descriptor_resp.handle = desc_result.handle;
195  characteristic_resp.descriptors.push_back(std::move(descriptor_resp));
196  desc_offset++;
197  }
198  service_resp.characteristics.push_back(std::move(characteristic_resp));
199  }
200  resp.services.push_back(std::move(service_resp));
202  }
203  }
204 }
205 
207  if (this->raw_advertisements_)
210 }
211 
213  for (auto *connection : this->connections_) {
214  if (connection->get_address() == address)
215  return connection;
216  }
217 
218  if (!reserve)
219  return nullptr;
220 
221  for (auto *connection : this->connections_) {
222  if (connection->get_address() == 0) {
223  connection->send_service_ = DONE_SENDING_SERVICES;
224  connection->set_address(address);
225  // All connections must start at INIT
226  // We only set the state if we allocate the connection
227  // to avoid a race where multiple connection attempts
228  // are made.
229  connection->set_state(espbt::ClientState::INIT);
230  return connection;
231  }
232  }
233 
234  return nullptr;
235 }
236 
238  switch (msg.request_type) {
242  auto *connection = this->get_connection_(msg.address, true);
243  if (connection == nullptr) {
244  ESP_LOGW(TAG, "No free connections available");
245  this->send_device_connection(msg.address, false);
246  return;
247  }
248  if (connection->state() == espbt::ClientState::CONNECTED ||
249  connection->state() == espbt::ClientState::ESTABLISHED) {
250  ESP_LOGW(TAG, "[%d] [%s] Connection already established", connection->get_connection_index(),
251  connection->address_str().c_str());
252  this->send_device_connection(msg.address, true);
253  this->send_connections_free();
254  return;
255  } else if (connection->state() == espbt::ClientState::SEARCHING) {
256  ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already searching for device",
257  connection->get_connection_index(), connection->address_str().c_str());
258  return;
259  } else if (connection->state() == espbt::ClientState::DISCOVERED) {
260  ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device already discovered",
261  connection->get_connection_index(), connection->address_str().c_str());
262  return;
263  } else if (connection->state() == espbt::ClientState::READY_TO_CONNECT) {
264  ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, waiting in line to connect",
265  connection->get_connection_index(), connection->address_str().c_str());
266  return;
267  } else if (connection->state() == espbt::ClientState::CONNECTING) {
268  ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already connecting", connection->get_connection_index(),
269  connection->address_str().c_str());
270  return;
271  } else if (connection->state() == espbt::ClientState::DISCONNECTING) {
272  ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, device is disconnecting",
273  connection->get_connection_index(), connection->address_str().c_str());
274  return;
275  } else if (connection->state() != espbt::ClientState::INIT) {
276  ESP_LOGW(TAG, "[%d] [%s] Connection already in progress", connection->get_connection_index(),
277  connection->address_str().c_str());
278  return;
279  }
281  connection->set_connection_type(espbt::ConnectionType::V3_WITH_CACHE);
282  ESP_LOGI(TAG, "[%d] [%s] Connecting v3 with cache", connection->get_connection_index(),
283  connection->address_str().c_str());
285  connection->set_connection_type(espbt::ConnectionType::V3_WITHOUT_CACHE);
286  ESP_LOGI(TAG, "[%d] [%s] Connecting v3 without cache", connection->get_connection_index(),
287  connection->address_str().c_str());
288  } else {
289  connection->set_connection_type(espbt::ConnectionType::V1);
290  ESP_LOGI(TAG, "[%d] [%s] Connecting v1", connection->get_connection_index(), connection->address_str().c_str());
291  }
292  if (msg.has_address_type) {
293  uint64_to_bd_addr(msg.address, connection->remote_bda_);
294  connection->set_remote_addr_type(static_cast<esp_ble_addr_type_t>(msg.address_type));
295  connection->set_state(espbt::ClientState::DISCOVERED);
296  } else {
297  connection->set_state(espbt::ClientState::SEARCHING);
298  }
299  this->send_connections_free();
300  break;
301  }
303  auto *connection = this->get_connection_(msg.address, false);
304  if (connection == nullptr) {
305  this->send_device_connection(msg.address, false);
306  this->send_connections_free();
307  return;
308  }
309  if (connection->state() != espbt::ClientState::IDLE) {
310  connection->disconnect();
311  } else {
312  connection->set_address(0);
313  this->send_device_connection(msg.address, false);
314  this->send_connections_free();
315  }
316  break;
317  }
319  auto *connection = this->get_connection_(msg.address, false);
320  if (connection != nullptr) {
321  if (!connection->is_paired()) {
322  auto err = connection->pair();
323  if (err != ESP_OK) {
324  this->send_device_pairing(msg.address, false, err);
325  }
326  } else {
327  this->send_device_pairing(msg.address, true);
328  }
329  }
330  break;
331  }
333  esp_bd_addr_t address;
334  uint64_to_bd_addr(msg.address, address);
335  esp_err_t ret = esp_ble_remove_bond_device(address);
336  this->send_device_pairing(msg.address, ret == ESP_OK, ret);
337  break;
338  }
340  esp_bd_addr_t address;
341  uint64_to_bd_addr(msg.address, address);
342  esp_err_t ret = esp_ble_gattc_cache_clean(address);
344  call.address = msg.address;
345  call.success = ret == ESP_OK;
346  call.error = ret;
347 
349 
350  break;
351  }
352  }
353 }
354 
356  auto *connection = this->get_connection_(msg.address, false);
357  if (connection == nullptr) {
358  ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected");
359  this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
360  return;
361  }
362 
363  auto err = connection->read_characteristic(msg.handle);
364  if (err != ESP_OK) {
365  this->send_gatt_error(msg.address, msg.handle, err);
366  }
367 }
368 
370  auto *connection = this->get_connection_(msg.address, false);
371  if (connection == nullptr) {
372  ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected");
373  this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
374  return;
375  }
376 
377  auto err = connection->write_characteristic(msg.handle, msg.data, msg.response);
378  if (err != ESP_OK) {
379  this->send_gatt_error(msg.address, msg.handle, err);
380  }
381 }
382 
384  auto *connection = this->get_connection_(msg.address, false);
385  if (connection == nullptr) {
386  ESP_LOGW(TAG, "Cannot read GATT descriptor, not connected");
387  this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
388  return;
389  }
390 
391  auto err = connection->read_descriptor(msg.handle);
392  if (err != ESP_OK) {
393  this->send_gatt_error(msg.address, msg.handle, err);
394  }
395 }
396 
398  auto *connection = this->get_connection_(msg.address, false);
399  if (connection == nullptr) {
400  ESP_LOGW(TAG, "Cannot write GATT descriptor, not connected");
401  this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
402  return;
403  }
404 
405  auto err = connection->write_descriptor(msg.handle, msg.data, true);
406  if (err != ESP_OK) {
407  this->send_gatt_error(msg.address, msg.handle, err);
408  }
409 }
410 
412  auto *connection = this->get_connection_(msg.address, false);
413  if (connection == nullptr || !connection->connected()) {
414  ESP_LOGW(TAG, "Cannot get GATT services, not connected");
415  this->send_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED);
416  return;
417  }
418  if (!connection->service_count_) {
419  ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str());
420  this->send_gatt_services_done(msg.address);
421  return;
422  }
423  if (connection->send_service_ ==
424  DONE_SENDING_SERVICES) // Only start sending services if we're not already sending them
425  connection->send_service_ = 0;
426 }
427 
429  auto *connection = this->get_connection_(msg.address, false);
430  if (connection == nullptr) {
431  ESP_LOGW(TAG, "Cannot notify GATT characteristic, not connected");
432  this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
433  return;
434  }
435 
436  auto err = connection->notify_characteristic(msg.handle, msg.enable);
437  if (err != ESP_OK) {
438  this->send_gatt_error(msg.address, msg.handle, err);
439  }
440 }
441 
443  if (this->api_connection_ != nullptr) {
444  ESP_LOGE(TAG, "Only one API subscription is allowed at a time");
445  return;
446  }
447  this->api_connection_ = api_connection;
450 }
451 
453  if (this->api_connection_ != api_connection) {
454  ESP_LOGV(TAG, "API connection is not subscribed");
455  return;
456  }
457  this->api_connection_ = nullptr;
458  this->raw_advertisements_ = false;
460 }
461 
462 void BluetoothProxy::send_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
463  if (this->api_connection_ == nullptr)
464  return;
466  call.address = address;
467  call.connected = connected;
468  call.mtu = mtu;
469  call.error = error;
471 }
473  if (this->api_connection_ == nullptr)
474  return;
476  call.free = this->get_bluetooth_connections_free();
477  call.limit = this->get_bluetooth_connections_limit();
479 }
480 
482  if (this->api_connection_ == nullptr)
483  return;
485  call.address = address;
487 }
488 
489 void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
490  if (this->api_connection_ == nullptr)
491  return;
493  call.address = address;
494  call.handle = handle;
495  call.error = error;
497 }
498 
499 void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
501  call.address = address;
502  call.paired = paired;
503  call.error = error;
504 
506 }
507 
508 void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
510  call.address = address;
511  call.success = success;
512  call.error = error;
513 
515 }
516 
517 BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
518 
519 } // namespace bluetooth_proxy
520 } // namespace esphome
521 
522 #endif // USE_ESP32
void send_device_pairing(uint64_t address, bool paired, esp_err_t error=ESP_OK)
std::vector< BluetoothGATTCharacteristic > characteristics
Definition: api_pb2.h:1486
BluetoothConnection * get_connection_(uint64_t address, bool reserve)
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address)
Definition: ble.cpp:427
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override
std::vector< BluetoothGATTService > services
Definition: api_pb2.h:1499
std::vector< BluetoothLERawAdvertisement > advertisements
Definition: api_pb2.h:1407
void send_device_unpairing(uint64_t address, bool success, esp_err_t error=ESP_OK)
bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg)
std::vector< BluetoothServiceData > service_data
Definition: api_pb2.h:1378
bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg)
const std::vector< ServiceData > & get_manufacturer_datas() const
const std::vector< ESPBTUUID > & get_service_uuids() const
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition: util.cpp:15
void bluetooth_device_request(const api::BluetoothDeviceRequest &msg)
BluetoothProxy * global_bluetooth_proxy
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid)
Definition: ble_uuid.cpp:97
std::vector< BluetoothGATTDescriptor > descriptors
Definition: api_pb2.h:1472
std::vector< uint64_t > uuid
Definition: api_pb2.h:1457
bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg)
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override
bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg)
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg)
void send_device_connection(uint64_t address, bool connected, uint16_t mtu=0, esp_err_t error=ESP_OK)
static void uint64_to_bd_addr(uint64_t address, esp_bd_addr_t bd_addr)
enums::BluetoothDeviceRequestType request_type
Definition: api_pb2.h:1419
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override
void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg)
std::vector< uint64_t > get_128bit_uuid_vec(esp_bt_uuid_t uuid_source)
const std::vector< ServiceData > & get_service_datas() const
void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg)
const uint32_t flags
Definition: stm32flash.h:85
esp_ble_addr_type_t get_address_type() const
void unsubscribe_api_connection(api::APIConnection *api_connection)
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg)
bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg)
std::vector< BluetoothConnection * > connections_
std::vector< uint64_t > uuid
Definition: api_pb2.h:1484
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device)
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg)
void send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error)
std::vector< BluetoothServiceData > manufacturer_data
Definition: api_pb2.h:1379
uint8_t address
Definition: bl0906.h:211
const std::string & get_name() const
std::vector< std::string > service_uuids
Definition: api_pb2.h:1377
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags)
bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg)
bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg)
ESPBTUUID as_128bit() const
Definition: ble_uuid.cpp:109
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg)
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg)
bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg)
APIServer * global_api_server
Definition: api_server.cpp:347
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg)
esp_bt_uuid_t get_uuid() const
Definition: ble_uuid.cpp:170