ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
bluetooth_connection.cpp
Go to the documentation of this file.
2
5#include "esphome/core/log.h"
6
7#ifdef USE_ESP32
8
9#include "bluetooth_proxy.h"
10
12
13static const char *const TAG = "bluetooth_proxy.connection";
14
15// This function is allocation-free and directly packs UUIDs into the output array
16// using precalculated constants for the Bluetooth base UUID
17static void fill_128bit_uuid_array(std::array<uint64_t, 2> &out, esp_bt_uuid_t uuid_source) {
18 // Bluetooth base UUID: 00000000-0000-1000-8000-00805F9B34FB
19 // out[0] = bytes 8-15 (big-endian)
20 // - For 128-bit UUIDs: use bytes 8-15 as-is
21 // - For 16/32-bit UUIDs: insert into bytes 12-15, use 0x00001000 for bytes 8-11
22 out[0] = uuid_source.len == ESP_UUID_LEN_128
23 ? (((uint64_t) uuid_source.uuid.uuid128[15] << 56) | ((uint64_t) uuid_source.uuid.uuid128[14] << 48) |
24 ((uint64_t) uuid_source.uuid.uuid128[13] << 40) | ((uint64_t) uuid_source.uuid.uuid128[12] << 32) |
25 ((uint64_t) uuid_source.uuid.uuid128[11] << 24) | ((uint64_t) uuid_source.uuid.uuid128[10] << 16) |
26 ((uint64_t) uuid_source.uuid.uuid128[9] << 8) | ((uint64_t) uuid_source.uuid.uuid128[8]))
27 : (((uint64_t) (uuid_source.len == ESP_UUID_LEN_16 ? uuid_source.uuid.uuid16 : uuid_source.uuid.uuid32)
28 << 32) |
29 0x00001000ULL); // Base UUID bytes 8-11
30 // out[1] = bytes 0-7 (big-endian)
31 // - For 128-bit UUIDs: use bytes 0-7 as-is
32 // - For 16/32-bit UUIDs: use precalculated base UUID constant
33 out[1] = uuid_source.len == ESP_UUID_LEN_128
34 ? ((uint64_t) uuid_source.uuid.uuid128[7] << 56) | ((uint64_t) uuid_source.uuid.uuid128[6] << 48) |
35 ((uint64_t) uuid_source.uuid.uuid128[5] << 40) | ((uint64_t) uuid_source.uuid.uuid128[4] << 32) |
36 ((uint64_t) uuid_source.uuid.uuid128[3] << 24) | ((uint64_t) uuid_source.uuid.uuid128[2] << 16) |
37 ((uint64_t) uuid_source.uuid.uuid128[1] << 8) | ((uint64_t) uuid_source.uuid.uuid128[0])
38 : 0x800000805F9B34FBULL; // Base UUID bytes 0-7: 80-00-00-80-5F-9B-34-FB
39}
40
41// Helper to fill UUID in the appropriate format based on client support and UUID type
42static void fill_gatt_uuid(std::array<uint64_t, 2> &uuid_128, uint32_t &short_uuid, const esp_bt_uuid_t &uuid,
43 bool use_efficient_uuids) {
44 if (!use_efficient_uuids || uuid.len == ESP_UUID_LEN_128) {
45 // Use 128-bit format for old clients or when UUID is already 128-bit
46 fill_128bit_uuid_array(uuid_128, uuid);
47 } else if (uuid.len == ESP_UUID_LEN_16) {
48 short_uuid = uuid.uuid.uuid16;
49 } else if (uuid.len == ESP_UUID_LEN_32) {
50 short_uuid = uuid.uuid.uuid32;
51 }
52}
53
54// Constants for size estimation
55static constexpr uint8_t SERVICE_OVERHEAD_LEGACY = 25; // UUID(20) + handle(4) + overhead(1)
56static constexpr uint8_t SERVICE_OVERHEAD_EFFICIENT = 10; // UUID(6) + handle(4)
57static constexpr uint8_t CHAR_SIZE_128BIT = 35; // UUID(20) + handle(4) + props(4) + overhead(7)
58static constexpr uint8_t DESC_SIZE_128BIT = 25; // UUID(20) + handle(4) + overhead(1)
59static constexpr uint8_t DESC_SIZE_16BIT = 10; // UUID(6) + handle(4)
60static constexpr uint8_t DESC_PER_CHAR = 1; // Assume 1 descriptor per characteristic
61
62// Helper to estimate service size before fetching all data
75static size_t estimate_service_size(uint16_t char_count, bool use_efficient_uuids) {
76 size_t service_overhead = use_efficient_uuids ? SERVICE_OVERHEAD_EFFICIENT : SERVICE_OVERHEAD_LEGACY;
77 // Always assume 128-bit UUIDs for characteristics to be safe
78 size_t char_size = CHAR_SIZE_128BIT;
79 // Assume one 128-bit descriptor per characteristic
80 size_t desc_size = DESC_SIZE_128BIT * DESC_PER_CHAR;
81
82 return service_overhead + (char_size + desc_size) * char_count;
83}
84
86 auto *api_conn = this->proxy_->get_api_connection();
87 return api_conn && api_conn->client_supports_api_version(1, 12);
88}
89
91 ESP_LOGCONFIG(TAG, "BLE Connection:");
93}
94
95void BluetoothConnection::update_allocated_slot_(uint64_t find_value, uint64_t set_value) {
96 auto &allocated = this->proxy_->connections_free_response_.allocated;
97 for (auto &slot : allocated) {
98 if (slot == find_value) {
99 slot = set_value;
100 return;
101 }
102 }
103}
104
106 // If we're clearing an address (disconnecting), update the pre-allocated message
107 if (address == 0 && this->address_ != 0) {
109 this->update_allocated_slot_(this->address_, 0);
110 }
111 // If we're setting a new address (connecting), update the pre-allocated message
112 else if (address != 0 && this->address_ == 0) {
114 this->update_allocated_slot_(0, address);
115 }
116
117 // Call parent implementation to actually set the address
119}
120
123
124 // Early return if no active connection
125 if (this->address_ == 0) {
126 return;
127 }
128
129 // Handle service discovery if in valid range
132 }
133
134 // Check if we should disable the loop
135 // - For V3_WITH_CACHE: Services are never sent, disable after INIT state
136 // - For V3_WITHOUT_CACHE: Disable only after service discovery is complete
137 // (send_service_ == DONE_SENDING_SERVICES, which is only set after services are sent)
138 // Never disable while DISCONNECTING — BLEClientBase::loop() needs to keep running so the
139 // 10s safety timeout can force IDLE if CLOSE_EVT is never delivered.
140 if (this->state() != espbt::ClientState::INIT && this->state() != espbt::ClientState::DISCONNECTING &&
141 (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
142 this->send_service_ == DONE_SENDING_SERVICES)) {
143 this->disable_loop();
144 }
145}
146
148 // Called from both the CLOSE_EVT handler and the DISCONNECTING safety timeout in the
149 // base class. Free the proxy slot, notify the API client, and reset send_service_.
150 // address_ may already be 0 if reset_connection_ ran earlier on this teardown.
151 if (this->address_ == 0) {
152 return;
153 }
154 ESP_LOGD(TAG, "[%d] [%s] Close, reason=0x%02x, freeing slot", this->connection_index_, this->address_str_, reason);
155 this->reset_connection_(reason);
156}
157
159 // Send disconnection notification
160 this->proxy_->send_device_connection(this->address_, false, 0, reason);
161
162 // Important: If we were in the middle of sending services, we do NOT send
163 // send_gatt_services_done() here. This ensures the client knows that
164 // the service discovery was interrupted and can retry. The client
165 // (aioesphomeapi) implements a 30-second timeout (DEFAULT_BLE_TIMEOUT)
166 // to detect incomplete service discovery rather than relying on us to
167 // tell them about a partial list.
168 this->set_address(0);
169 this->send_service_ = INIT_SENDING_SERVICES;
171}
172
174 if (this->send_service_ >= this->service_count_) {
175 this->send_service_ = DONE_SENDING_SERVICES;
177 this->release_services();
178 return;
179 }
180
181 // Early return if no API connection
182 auto *api_conn = this->proxy_->get_api_connection();
183 if (api_conn == nullptr) {
184 this->send_service_ = DONE_SENDING_SERVICES;
185 return;
186 }
187
188 // Check if client supports efficient UUIDs
189 bool use_efficient_uuids = this->supports_efficient_uuids_();
190
191 // Prepare response
193 resp.address = this->address_;
194
195 // Dynamic batching based on actual size
196 // Conservative MTU limit for API messages (accounts for WPA3 overhead)
197 static constexpr size_t MAX_PACKET_SIZE = 1360;
198
199 // Keep running total of actual message size
200 size_t current_size = resp.calculate_size();
201
203 esp_gattc_service_elem_t service_result;
204 uint16_t service_count = 1;
205 esp_gatt_status_t service_status = esp_ble_gattc_get_service(this->gattc_if_, this->conn_id_, nullptr,
206 &service_result, &service_count, this->send_service_);
207
208 if (service_status != ESP_GATT_OK || service_count == 0) {
209 ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_get_service %s, status=%d, service_count=%d, offset=%d",
210 this->connection_index_, this->address_str(), service_status != ESP_GATT_OK ? "error" : "missing",
211 service_status, service_count, this->send_service_);
212 this->send_service_ = DONE_SENDING_SERVICES;
213 return;
214 }
215
216 // Get the number of characteristics BEFORE adding to response
217 uint16_t total_char_count = 0;
218 esp_gatt_status_t char_count_status =
219 esp_ble_gattc_get_attr_count(this->gattc_if_, this->conn_id_, ESP_GATT_DB_CHARACTERISTIC,
220 service_result.start_handle, service_result.end_handle, 0, &total_char_count);
221
222 if (char_count_status != ESP_GATT_OK) {
223 this->log_connection_error_("esp_ble_gattc_get_attr_count", char_count_status);
224 this->send_service_ = DONE_SENDING_SERVICES;
225 return;
226 }
227
228 // If this service likely won't fit, send current batch (unless it's the first)
229 size_t estimated_size = estimate_service_size(total_char_count, use_efficient_uuids);
230 if (!resp.services.empty() && (current_size + estimated_size > MAX_PACKET_SIZE)) {
231 // This service likely won't fit, send current batch
232 break;
233 }
234
235 // Now add the service since we know it will likely fit
236 resp.services.emplace_back();
237 auto &service_resp = resp.services.back();
238
239 fill_gatt_uuid(service_resp.uuid, service_resp.short_uuid, service_result.uuid, use_efficient_uuids);
240
241 service_resp.handle = service_result.start_handle;
242
243 if (total_char_count > 0) {
244 // Initialize FixedVector with exact count and process characteristics
245 service_resp.characteristics.init(total_char_count);
246 uint16_t char_offset = 0;
247 esp_gattc_char_elem_t char_result;
248 // Bound by total_char_count: the vector is sized for it, and a malicious peripheral
249 // can make enumeration return more entries than the count query reported
250 while (char_offset < total_char_count) { // characteristics
251 uint16_t char_count = 1;
252 esp_gatt_status_t char_status =
253 esp_ble_gattc_get_all_char(this->gattc_if_, this->conn_id_, service_result.start_handle,
254 service_result.end_handle, &char_result, &char_count, char_offset);
255 if (char_status == ESP_GATT_INVALID_OFFSET || char_status == ESP_GATT_NOT_FOUND) {
256 break;
257 }
258 if (char_status != ESP_GATT_OK) {
259 this->log_connection_error_("esp_ble_gattc_get_all_char", char_status);
260 this->send_service_ = DONE_SENDING_SERVICES;
261 return;
262 }
263 if (char_count == 0) {
264 break;
265 }
266
267 service_resp.characteristics.emplace_back();
268 auto &characteristic_resp = service_resp.characteristics.back();
269 fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, char_result.uuid, use_efficient_uuids);
270 characteristic_resp.handle = char_result.char_handle;
271 characteristic_resp.properties = char_result.properties;
272 char_offset++;
273
274 // Get the number of descriptors directly with one call
275 uint16_t total_desc_count = 0;
276 esp_gatt_status_t desc_count_status = esp_ble_gattc_get_attr_count(
277 this->gattc_if_, this->conn_id_, ESP_GATT_DB_DESCRIPTOR, 0, 0, char_result.char_handle, &total_desc_count);
278
279 if (desc_count_status != ESP_GATT_OK) {
280 this->log_connection_error_("esp_ble_gattc_get_attr_count", desc_count_status);
281 this->send_service_ = DONE_SENDING_SERVICES;
282 return;
283 }
284 if (total_desc_count == 0) {
285 continue;
286 }
287
288 // Initialize FixedVector with exact count and process descriptors
289 characteristic_resp.descriptors.init(total_desc_count);
290 uint16_t desc_offset = 0;
291 esp_gattc_descr_elem_t desc_result;
292 while (desc_offset < total_desc_count) { // descriptors
293 uint16_t desc_count = 1;
294 esp_gatt_status_t desc_status = esp_ble_gattc_get_all_descr(
295 this->gattc_if_, this->conn_id_, char_result.char_handle, &desc_result, &desc_count, desc_offset);
296 if (desc_status == ESP_GATT_INVALID_OFFSET || desc_status == ESP_GATT_NOT_FOUND) {
297 break;
298 }
299 if (desc_status != ESP_GATT_OK) {
300 this->log_connection_error_("esp_ble_gattc_get_all_descr", desc_status);
301 this->send_service_ = DONE_SENDING_SERVICES;
302 return;
303 }
304 if (desc_count == 0) {
305 break; // No more descriptors
306 }
307
308 characteristic_resp.descriptors.emplace_back();
309 auto &descriptor_resp = characteristic_resp.descriptors.back();
310 fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc_result.uuid, use_efficient_uuids);
311 descriptor_resp.handle = desc_result.handle;
312 desc_offset++;
313 }
314 }
315 } // end if (total_char_count > 0)
316
317 // Calculate the actual size of just this service
318 size_t service_size = service_resp.calculate_size() + 1; // +1 for field tag
319
320 // Check if adding this service would exceed the limit
321 if (current_size + service_size > MAX_PACKET_SIZE) {
322 // We would go over - pop the last service if we have more than one
323 if (resp.services.size() > 1) {
324 resp.services.pop_back();
325 ESP_LOGD(TAG, "[%d] [%s] Service %d would exceed limit (current: %d + service: %d > %d), sending current batch",
326 this->connection_index_, this->address_str(), this->send_service_, current_size, service_size,
327 MAX_PACKET_SIZE);
328 // Don't increment send_service_ - we'll retry this service in next batch
329 } else {
330 // This single service is too large, but we have to send it anyway
331 ESP_LOGV(TAG, "[%d] [%s] Service %d is too large (%d bytes) but sending anyway", this->connection_index_,
332 this->address_str(), this->send_service_, service_size);
333 // Increment so we don't get stuck
334 this->send_service_++;
335 }
336 // Send what we have
337 break;
338 }
339
340 // Now we know we're keeping this service, add its size
341 current_size += service_size;
342 // Successfully added this service, increment counter
343 this->send_service_++;
344 }
345
346 // Send the message with dynamically batched services
347 api_conn->send_message(resp);
348}
349
350void BluetoothConnection::log_connection_error_(const char *operation, esp_gatt_status_t status) {
351 ESP_LOGE(TAG, "[%d] [%s] %s error, status=%d", this->connection_index_, this->address_str(), operation, status);
352}
353
354void BluetoothConnection::log_connection_warning_(const char *operation, esp_err_t err) {
355 ESP_LOGW(TAG, "[%d] [%s] %s failed, err=%d", this->connection_index_, this->address_str(), operation, err);
356}
357
358void BluetoothConnection::log_gatt_not_connected_(const char *action, const char *type) {
359 ESP_LOGW(TAG, "[%d] [%s] Cannot %s GATT %s, not connected.", this->connection_index_, this->address_str(), action,
360 type);
361}
362
363void BluetoothConnection::log_gatt_operation_error_(const char *operation, uint16_t handle, esp_gatt_status_t status) {
364 ESP_LOGW(TAG, "[%d] [%s] Error %s for handle 0x%2X, status=%d", this->connection_index_, this->address_str(),
365 operation, handle, status);
366}
367
368esp_err_t BluetoothConnection::check_and_log_error_(const char *operation, esp_err_t err) {
369 if (err != ESP_OK) {
370 this->log_connection_warning_(operation, err);
371 return err;
372 }
373 return ESP_OK;
374}
375
376bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
377 esp_ble_gattc_cb_param_t *param) {
378 if (!BLEClientBase::gattc_event_handler(event, gattc_if, param))
379 return false;
380
381 switch (event) {
382 case ESP_GATTC_DISCONNECT_EVT: {
383 // Don't reset connection yet - wait for CLOSE_EVT to ensure controller has freed resources
384 // This prevents race condition where we mark slot as free before controller cleanup is complete
385 ESP_LOGD(TAG, "[%d] [%s] Disconnect, reason=0x%02x", this->connection_index_, this->address_str_,
386 param->disconnect.reason);
387 // Send disconnection notification but don't free the slot yet
388 this->proxy_->send_device_connection(this->address_, false, 0, param->disconnect.reason);
389 break;
390 }
391 case ESP_GATTC_OPEN_EVT: {
392 if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
393 this->reset_connection_(param->open.status);
394 } else if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
395 this->proxy_->send_device_connection(this->address_, true, this->mtu_);
397 }
398 this->seen_mtu_or_services_ = false;
399 break;
400 }
401 case ESP_GATTC_CFG_MTU_EVT:
402 case ESP_GATTC_SEARCH_CMPL_EVT: {
403 if (!this->seen_mtu_or_services_) {
404 // We don't know if we will get the MTU or the services first, so
405 // only send the device connection true if we have already received
406 // the services.
407 this->seen_mtu_or_services_ = true;
408 break;
409 }
410 this->proxy_->send_device_connection(this->address_, true, this->mtu_);
412 break;
413 }
414 case ESP_GATTC_READ_DESCR_EVT:
415 case ESP_GATTC_READ_CHAR_EVT: {
416 if (param->read.status != ESP_GATT_OK) {
417 this->log_gatt_operation_error_("reading char/descriptor", param->read.handle, param->read.status);
418 this->proxy_->send_gatt_error(this->address_, param->read.handle, param->read.status);
419 break;
420 }
421 auto *api_connection = this->proxy_->get_api_connection();
422 if (api_connection == nullptr)
423 break;
425 resp.address = this->address_;
426 resp.handle = param->read.handle;
427 resp.set_data(param->read.value, param->read.value_len);
428 api_connection->send_message(resp);
429 break;
430 }
431 case ESP_GATTC_WRITE_CHAR_EVT:
432 case ESP_GATTC_WRITE_DESCR_EVT: {
433 if (param->write.status != ESP_GATT_OK) {
434 this->log_gatt_operation_error_("writing char/descriptor", param->write.handle, param->write.status);
435 this->proxy_->send_gatt_error(this->address_, param->write.handle, param->write.status);
436 break;
437 }
438 auto *api_connection = this->proxy_->get_api_connection();
439 if (api_connection == nullptr)
440 break;
442 resp.address = this->address_;
443 resp.handle = param->write.handle;
444 api_connection->send_message(resp);
445 break;
446 }
447 case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
448 if (param->unreg_for_notify.status != ESP_GATT_OK) {
449 this->log_gatt_operation_error_("unregistering notifications", param->unreg_for_notify.handle,
450 param->unreg_for_notify.status);
451 this->proxy_->send_gatt_error(this->address_, param->unreg_for_notify.handle, param->unreg_for_notify.status);
452 break;
453 }
454 auto *api_connection = this->proxy_->get_api_connection();
455 if (api_connection == nullptr)
456 break;
458 resp.address = this->address_;
459 resp.handle = param->unreg_for_notify.handle;
460 api_connection->send_message(resp);
461 break;
462 }
463 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
464 if (param->reg_for_notify.status != ESP_GATT_OK) {
465 this->log_gatt_operation_error_("registering notifications", param->reg_for_notify.handle,
466 param->reg_for_notify.status);
467 this->proxy_->send_gatt_error(this->address_, param->reg_for_notify.handle, param->reg_for_notify.status);
468 break;
469 }
470 auto *api_connection = this->proxy_->get_api_connection();
471 if (api_connection == nullptr)
472 break;
474 resp.address = this->address_;
475 resp.handle = param->reg_for_notify.handle;
476 api_connection->send_message(resp);
477 break;
478 }
479 case ESP_GATTC_NOTIFY_EVT: {
480 ESP_LOGV(TAG, "[%d] [%s] ESP_GATTC_NOTIFY_EVT: handle=0x%2X", this->connection_index_, this->address_str_,
481 param->notify.handle);
482 auto *api_connection = this->proxy_->get_api_connection();
483 if (api_connection == nullptr)
484 break;
486 resp.address = this->address_;
487 resp.handle = param->notify.handle;
488 resp.set_data(param->notify.value, param->notify.value_len);
489 api_connection->send_message(resp);
490 break;
491 }
492 default:
493 break;
494 }
495 return true;
496}
497
498void BluetoothConnection::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
500
501 switch (event) {
502 case ESP_GAP_BLE_AUTH_CMPL_EVT:
503 if (memcmp(param->ble_security.auth_cmpl.bd_addr, this->remote_bda_, 6) != 0)
504 break;
505 if (param->ble_security.auth_cmpl.success) {
506 this->proxy_->send_device_pairing(this->address_, true);
507 } else {
508 this->proxy_->send_device_pairing(this->address_, false, param->ble_security.auth_cmpl.fail_reason);
509 }
510 break;
511 default:
512 break;
513 }
514}
515
517 if (!this->connected()) {
518 this->log_gatt_not_connected_("read", "characteristic");
519 return ESP_GATT_NOT_CONNECTED;
520 }
521
522 ESP_LOGV(TAG, "[%d] [%s] Reading GATT characteristic handle %d", this->connection_index_, this->address_str_, handle);
523
524 esp_err_t err = esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE);
525 return this->check_and_log_error_("esp_ble_gattc_read_char", err);
526}
527
528esp_err_t BluetoothConnection::write_characteristic(uint16_t handle, const uint8_t *data, size_t length,
529 bool response) {
530 if (!this->connected()) {
531 this->log_gatt_not_connected_("write", "characteristic");
532 return ESP_GATT_NOT_CONNECTED;
533 }
534 ESP_LOGV(TAG, "[%d] [%s] Writing GATT characteristic handle %d", this->connection_index_, this->address_str_, handle);
535
536 // ESP-IDF's API requires a non-const uint8_t* but it doesn't modify the data
537 // The BTC layer immediately copies the data to its own buffer (see btc_gattc.c)
538 // const_cast is safe here and was previously hidden by a C-style cast
539 esp_err_t err =
540 esp_ble_gattc_write_char(this->gattc_if_, this->conn_id_, handle, length, const_cast<uint8_t *>(data),
541 response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
542 return this->check_and_log_error_("esp_ble_gattc_write_char", err);
543}
544
546 if (!this->connected()) {
547 this->log_gatt_not_connected_("read", "descriptor");
548 return ESP_GATT_NOT_CONNECTED;
549 }
550 ESP_LOGV(TAG, "[%d] [%s] Reading GATT descriptor handle %d", this->connection_index_, this->address_str_, handle);
551
552 esp_err_t err = esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, handle, ESP_GATT_AUTH_REQ_NONE);
553 return this->check_and_log_error_("esp_ble_gattc_read_char_descr", err);
554}
555
556esp_err_t BluetoothConnection::write_descriptor(uint16_t handle, const uint8_t *data, size_t length, bool response) {
557 if (!this->connected()) {
558 this->log_gatt_not_connected_("write", "descriptor");
559 return ESP_GATT_NOT_CONNECTED;
560 }
561 ESP_LOGV(TAG, "[%d] [%s] Writing GATT descriptor handle %d", this->connection_index_, this->address_str_, handle);
562
563 // ESP-IDF's API requires a non-const uint8_t* but it doesn't modify the data
564 // The BTC layer immediately copies the data to its own buffer (see btc_gattc.c)
565 // const_cast is safe here and was previously hidden by a C-style cast
566 esp_err_t err = esp_ble_gattc_write_char_descr(
567 this->gattc_if_, this->conn_id_, handle, length, const_cast<uint8_t *>(data),
568 response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
569 return this->check_and_log_error_("esp_ble_gattc_write_char_descr", err);
570}
571
572esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enable) {
573 if (!this->connected()) {
574 this->log_gatt_not_connected_("notify", "characteristic");
575 return ESP_GATT_NOT_CONNECTED;
576 }
577
578 if (enable) {
579 ESP_LOGV(TAG, "[%d] [%s] Registering for GATT characteristic notifications handle %d", this->connection_index_,
580 this->address_str_, handle);
581 esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, handle);
582 return this->check_and_log_error_("esp_ble_gattc_register_for_notify", err);
583 }
584
585 ESP_LOGV(TAG, "[%d] [%s] Unregistering for GATT characteristic notifications handle %d", this->connection_index_,
586 this->address_str_, handle);
587 esp_err_t err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, handle);
588 return this->check_and_log_error_("esp_ble_gattc_unregister_for_notify", err);
589}
590
594
595} // namespace esphome::bluetooth_proxy
596
597#endif // USE_ESP32
uint8_t address
Definition bl0906.h:4
uint8_t status
Definition bl0942.h:8
void disable_loop()
Disable this component's loop.
bool client_supports_api_version(uint16_t major, uint16_t minor) const
std::array< uint64_t, BLUETOOTH_PROXY_MAX_CONNECTIONS > allocated
Definition api_pb2.h:2204
std::vector< BluetoothGATTService > services
Definition api_pb2.h:2036
void set_data(const uint8_t *data, size_t len)
Definition api_pb2.h:2183
void set_data(const uint8_t *data, size_t len)
Definition api_pb2.h:2088
void log_connection_error_(const char *operation, esp_gatt_status_t status)
void log_gatt_operation_error_(const char *operation, uint16_t handle, esp_gatt_status_t status)
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override
esp_err_t write_characteristic(uint16_t handle, const uint8_t *data, size_t length, bool response)
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
void log_connection_warning_(const char *operation, esp_err_t err)
esp_err_t check_and_log_error_(const char *operation, esp_err_t err)
esp_err_t write_descriptor(uint16_t handle, const uint8_t *data, size_t length, bool response)
esp_err_t notify_characteristic(uint16_t handle, bool enable)
void on_disconnect_complete(esp_err_t reason) override
void log_gatt_not_connected_(const char *action, const char *type)
void update_allocated_slot_(uint64_t find_value, uint64_t set_value)
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override
void send_device_connection(uint64_t address, bool connected, uint16_t mtu=0, esp_err_t error=ESP_OK)
void send_device_pairing(uint64_t address, bool paired, esp_err_t error=ESP_OK)
void send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error)
api::BluetoothConnectionsFreeResponse connections_free_response_
char address_str_[MAC_ADDRESS_PRETTY_BUFFER_SIZE]
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
virtual void set_address(uint64_t address)
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
uint16_t type
static void uint32_t
uint16_t length
Definition tt21100.cpp:0
spi_device_handle_t handle