ESPHome 2025.7.1
Loading...
Searching...
No Matches
ble_client_base.cpp
Go to the documentation of this file.
1#include "ble_client_base.h"
2
4#include "esphome/core/log.h"
5
6#ifdef USE_ESP32
7
8namespace esphome {
9namespace esp32_ble_client {
10
11static const char *const TAG = "esp32_ble_client";
12static const esp_bt_uuid_t NOTIFY_DESC_UUID = {
13 .len = ESP_UUID_LEN_16,
14 .uuid =
15 {
16 .uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,
17 },
18};
19
21 static uint8_t connection_index = 0;
22 this->connection_index_ = connection_index++;
23}
24
26 ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st);
27 ESPBTClient::set_state(st);
28
29 if (st == espbt::ClientState::READY_TO_CONNECT) {
30 // Enable loop when we need to connect
31 this->enable_loop();
32 }
33}
34
36 if (!esp32_ble::global_ble->is_active()) {
37 this->set_state(espbt::ClientState::INIT);
38 return;
39 }
40 if (this->state_ == espbt::ClientState::INIT) {
41 auto ret = esp_ble_gattc_app_register(this->app_id);
42 if (ret) {
43 ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
44 this->mark_failed();
45 }
46 this->set_state(espbt::ClientState::IDLE);
47 }
48 // READY_TO_CONNECT means we have discovered the device
49 // and the scanner has been stopped by the tracker.
50 else if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
51 this->connect();
52 }
53 // If its idle, we can disable the loop as set_state
54 // will enable it again when we need to connect.
55 else if (this->state_ == espbt::ClientState::IDLE) {
56 this->disable_loop();
57 }
58}
59
61
63 ESP_LOGCONFIG(TAG,
64 " Address: %s\n"
65 " Auto-Connect: %s",
66 this->address_str().c_str(), TRUEFALSE(this->auto_connect_));
67 std::string state_name;
68 switch (this->state()) {
69 case espbt::ClientState::INIT:
70 state_name = "INIT";
71 break;
72 case espbt::ClientState::DISCONNECTING:
73 state_name = "DISCONNECTING";
74 break;
75 case espbt::ClientState::IDLE:
76 state_name = "IDLE";
77 break;
78 case espbt::ClientState::SEARCHING:
79 state_name = "SEARCHING";
80 break;
81 case espbt::ClientState::DISCOVERED:
82 state_name = "DISCOVERED";
83 break;
84 case espbt::ClientState::READY_TO_CONNECT:
85 state_name = "READY_TO_CONNECT";
86 break;
87 case espbt::ClientState::CONNECTING:
88 state_name = "CONNECTING";
89 break;
90 case espbt::ClientState::CONNECTED:
91 state_name = "CONNECTED";
92 break;
93 case espbt::ClientState::ESTABLISHED:
94 state_name = "ESTABLISHED";
95 break;
96 default:
97 state_name = "UNKNOWN_STATE";
98 break;
99 }
100 ESP_LOGCONFIG(TAG, " State: %s", state_name.c_str());
101 if (this->status_ == ESP_GATT_NO_RESOURCES) {
102 ESP_LOGE(TAG, " Failed due to no resources. Try to reduce number of BLE clients in config.");
103 } else if (this->status_ != ESP_GATT_OK) {
104 ESP_LOGW(TAG, " Failed due to error code %d", this->status_);
105 }
106}
107
109 if (!this->auto_connect_)
110 return false;
111 if (this->address_ == 0 || device.address_uint64() != this->address_)
112 return false;
113 if (this->state_ != espbt::ClientState::IDLE && this->state_ != espbt::ClientState::SEARCHING)
114 return false;
115
116 this->log_event_("Found device");
117 if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG)
119
120 this->set_state(espbt::ClientState::DISCOVERED);
121 this->set_address(device.address_uint64());
122 this->remote_addr_type_ = device.get_address_type();
123 return true;
124}
125
127 ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),
128 this->remote_addr_type_);
129 this->paired_ = false;
130 auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true);
131 if (ret) {
132 ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(),
133 ret);
134 this->set_state(espbt::ClientState::IDLE);
135 } else {
136 this->set_state(espbt::ClientState::CONNECTING);
137 }
138}
139
140esp_err_t BLEClientBase::pair() { return esp_ble_set_encryption(this->remote_bda_, ESP_BLE_SEC_ENCRYPT); }
141
143 if (this->state_ == espbt::ClientState::IDLE) {
144 ESP_LOGI(TAG, "[%d] [%s] Disconnect requested, but already idle.", this->connection_index_,
145 this->address_str_.c_str());
146 return;
147 }
148 if (this->state_ == espbt::ClientState::DISCONNECTING) {
149 ESP_LOGI(TAG, "[%d] [%s] Disconnect requested, but already disconnecting.", this->connection_index_,
150 this->address_str_.c_str());
151 return;
152 }
153 if (this->state_ == espbt::ClientState::CONNECTING || this->conn_id_ == UNSET_CONN_ID) {
154 ESP_LOGW(TAG, "[%d] [%s] Disconnecting before connected, disconnect scheduled.", this->connection_index_,
155 this->address_str_.c_str());
156 this->want_disconnect_ = true;
157 return;
158 }
160}
161
163 // Disconnect without checking the state.
164 ESP_LOGI(TAG, "[%d] [%s] Disconnecting (conn_id: %d).", this->connection_index_, this->address_str_.c_str(),
165 this->conn_id_);
166 if (this->state_ == espbt::ClientState::DISCONNECTING) {
167 ESP_LOGE(TAG, "[%d] [%s] Tried to disconnect while already disconnecting.", this->connection_index_,
168 this->address_str_.c_str());
169 return;
170 }
171 if (this->conn_id_ == UNSET_CONN_ID) {
172 ESP_LOGE(TAG, "[%d] [%s] No connection ID set, cannot disconnect.", this->connection_index_,
173 this->address_str_.c_str());
174 return;
175 }
176 auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_);
177 if (err != ESP_OK) {
178 //
179 // This is a fatal error, but we can't do anything about it
180 // and it likely means the BLE stack is in a bad state.
181 //
182 // In the future we might consider App.reboot() here since
183 // the BLE stack is in an indeterminate state.
184 //
185 ESP_LOGE(TAG, "[%d] [%s] esp_ble_gattc_close error, err=%d", this->connection_index_, this->address_str_.c_str(),
186 err);
187 }
188
189 if (this->state_ == espbt::ClientState::SEARCHING || this->state_ == espbt::ClientState::READY_TO_CONNECT ||
190 this->state_ == espbt::ClientState::DISCOVERED) {
191 this->set_address(0);
192 this->set_state(espbt::ClientState::IDLE);
193 } else {
194 this->set_state(espbt::ClientState::DISCONNECTING);
195 }
196}
197
199 for (auto &svc : this->services_)
200 delete svc; // NOLINT(cppcoreguidelines-owning-memory)
201 this->services_.clear();
202#ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH
203 esp_ble_gattc_cache_clean(this->remote_bda_);
204#endif
205}
206
207void BLEClientBase::log_event_(const char *name) {
208 ESP_LOGD(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_.c_str(), name);
209}
210
211bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if,
212 esp_ble_gattc_cb_param_t *param) {
213 if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
214 return false;
215 if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if_)
216 return false;
217
218 ESP_LOGV(TAG, "[%d] [%s] gattc_event_handler: event=%d gattc_if=%d", this->connection_index_,
219 this->address_str_.c_str(), event, esp_gattc_if);
220
221 switch (event) {
222 case ESP_GATTC_REG_EVT: {
223 if (param->reg.status == ESP_GATT_OK) {
224 ESP_LOGV(TAG, "[%d] [%s] gattc registered app id %d", this->connection_index_, this->address_str_.c_str(),
225 this->app_id);
226 this->gattc_if_ = esp_gattc_if;
227 } else {
228 ESP_LOGE(TAG, "[%d] [%s] gattc app registration failed id=%d code=%d", this->connection_index_,
229 this->address_str_.c_str(), param->reg.app_id, param->reg.status);
230 this->status_ = param->reg.status;
231 this->mark_failed();
232 }
233 break;
234 }
235 case ESP_GATTC_OPEN_EVT: {
236 if (!this->check_addr(param->open.remote_bda))
237 return false;
238 this->log_event_("ESP_GATTC_OPEN_EVT");
239 this->conn_id_ = param->open.conn_id;
240 this->service_count_ = 0;
241 if (this->state_ != espbt::ClientState::CONNECTING) {
242 // This should not happen but lets log it in case it does
243 // because it means we have a bad assumption about how the
244 // ESP BT stack works.
245 if (this->state_ == espbt::ClientState::CONNECTED) {
246 ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT while already connected, status=%d", this->connection_index_,
247 this->address_str_.c_str(), param->open.status);
248 } else if (this->state_ == espbt::ClientState::ESTABLISHED) {
249 ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT while already established, status=%d",
250 this->connection_index_, this->address_str_.c_str(), param->open.status);
251 } else if (this->state_ == espbt::ClientState::DISCONNECTING) {
252 ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT while disconnecting, status=%d", this->connection_index_,
253 this->address_str_.c_str(), param->open.status);
254 } else {
255 ESP_LOGE(TAG, "[%d] [%s] Got ESP_GATTC_OPEN_EVT while not in connecting state, status=%d",
256 this->connection_index_, this->address_str_.c_str(), param->open.status);
257 }
258 }
259 if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) {
260 ESP_LOGW(TAG, "[%d] [%s] Connection failed, status=%d", this->connection_index_, this->address_str_.c_str(),
261 param->open.status);
262 this->set_state(espbt::ClientState::IDLE);
263 break;
264 }
265 if (this->want_disconnect_) {
266 // Disconnect was requested after connecting started,
267 // but before the connection was established. Now that we have
268 // this->conn_id_ set, we can disconnect it.
270 this->conn_id_ = UNSET_CONN_ID;
271 break;
272 }
273 auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if_, param->open.conn_id);
274 if (ret) {
275 ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_send_mtu_req failed, status=%x", this->connection_index_,
276 this->address_str_.c_str(), ret);
277 }
278 this->set_state(espbt::ClientState::CONNECTED);
279 if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
280 ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
281 // only set our state, subclients might have more stuff to do yet.
282 this->state_ = espbt::ClientState::ESTABLISHED;
283 break;
284 }
285 esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
286 break;
287 }
288 case ESP_GATTC_CONNECT_EVT: {
289 if (!this->check_addr(param->connect.remote_bda))
290 return false;
291 this->log_event_("ESP_GATTC_CONNECT_EVT");
292 break;
293 }
294 case ESP_GATTC_DISCONNECT_EVT: {
295 if (!this->check_addr(param->disconnect.remote_bda))
296 return false;
297 ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_,
298 this->address_str_.c_str(), param->disconnect.reason);
299 this->release_services();
300 this->set_state(espbt::ClientState::IDLE);
301 break;
302 }
303
304 case ESP_GATTC_CFG_MTU_EVT: {
305 if (this->conn_id_ != param->cfg_mtu.conn_id)
306 return false;
307 if (param->cfg_mtu.status != ESP_GATT_OK) {
308 ESP_LOGW(TAG, "[%d] [%s] cfg_mtu failed, mtu %d, status %d", this->connection_index_,
309 this->address_str_.c_str(), param->cfg_mtu.mtu, param->cfg_mtu.status);
310 // No state change required here - disconnect event will follow if needed.
311 break;
312 }
313 ESP_LOGD(TAG, "[%d] [%s] cfg_mtu status %d, mtu %d", this->connection_index_, this->address_str_.c_str(),
314 param->cfg_mtu.status, param->cfg_mtu.mtu);
315 this->mtu_ = param->cfg_mtu.mtu;
316 break;
317 }
318 case ESP_GATTC_CLOSE_EVT: {
319 if (this->conn_id_ != param->close.conn_id)
320 return false;
321 this->log_event_("ESP_GATTC_CLOSE_EVT");
322 this->release_services();
323 this->set_state(espbt::ClientState::IDLE);
324 this->conn_id_ = UNSET_CONN_ID;
325 break;
326 }
327 case ESP_GATTC_SEARCH_RES_EVT: {
328 if (this->conn_id_ != param->search_res.conn_id)
329 return false;
330 this->service_count_++;
331 if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
332 // V3 clients don't need services initialized since
333 // they only request by handle after receiving the services.
334 break;
335 }
336 BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory)
337 ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
338 ble_service->start_handle = param->search_res.start_handle;
339 ble_service->end_handle = param->search_res.end_handle;
340 ble_service->client = this;
341 this->services_.push_back(ble_service);
342 break;
343 }
344 case ESP_GATTC_SEARCH_CMPL_EVT: {
345 if (this->conn_id_ != param->search_cmpl.conn_id)
346 return false;
347 this->log_event_("ESP_GATTC_SEARCH_CMPL_EVT");
348 for (auto &svc : this->services_) {
349 ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_.c_str(),
350 svc->uuid.to_string().c_str());
351 ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_,
352 this->address_str_.c_str(), svc->start_handle, svc->end_handle);
353 }
354 ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str());
355 this->state_ = espbt::ClientState::ESTABLISHED;
356 break;
357 }
358 case ESP_GATTC_READ_DESCR_EVT: {
359 if (this->conn_id_ != param->write.conn_id)
360 return false;
361 this->log_event_("ESP_GATTC_READ_DESCR_EVT");
362 break;
363 }
364 case ESP_GATTC_WRITE_DESCR_EVT: {
365 if (this->conn_id_ != param->write.conn_id)
366 return false;
367 this->log_event_("ESP_GATTC_WRITE_DESCR_EVT");
368 break;
369 }
370 case ESP_GATTC_WRITE_CHAR_EVT: {
371 if (this->conn_id_ != param->write.conn_id)
372 return false;
373 this->log_event_("ESP_GATTC_WRITE_CHAR_EVT");
374 break;
375 }
376 case ESP_GATTC_READ_CHAR_EVT: {
377 if (this->conn_id_ != param->read.conn_id)
378 return false;
379 this->log_event_("ESP_GATTC_READ_CHAR_EVT");
380 break;
381 }
382 case ESP_GATTC_NOTIFY_EVT: {
383 if (this->conn_id_ != param->notify.conn_id)
384 return false;
385 this->log_event_("ESP_GATTC_NOTIFY_EVT");
386 break;
387 }
388 case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
389 this->log_event_("ESP_GATTC_REG_FOR_NOTIFY_EVT");
390 if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
391 this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
392 // Client is responsible for flipping the descriptor value
393 // when using the cache
394 break;
395 }
396 esp_gattc_descr_elem_t desc_result;
397 uint16_t count = 1;
398 esp_gatt_status_t descr_status = esp_ble_gattc_get_descr_by_char_handle(
399 this->gattc_if_, this->conn_id_, param->reg_for_notify.handle, NOTIFY_DESC_UUID, &desc_result, &count);
400 if (descr_status != ESP_GATT_OK) {
401 ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_descr_by_char_handle error, status=%d", this->connection_index_,
402 this->address_str_.c_str(), descr_status);
403 break;
404 }
405 esp_gattc_char_elem_t char_result;
406 esp_gatt_status_t char_status =
407 esp_ble_gattc_get_all_char(this->gattc_if_, this->conn_id_, param->reg_for_notify.handle,
408 param->reg_for_notify.handle, &char_result, &count, 0);
409 if (char_status != ESP_GATT_OK) {
410 ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_get_all_char error, status=%d", this->connection_index_,
411 this->address_str_.c_str(), char_status);
412 break;
413 }
414
415 /*
416 1 = notify
417 2 = indicate
418 */
419 uint16_t notify_en = char_result.properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY ? 1 : 2;
420 esp_err_t status =
421 esp_ble_gattc_write_char_descr(this->gattc_if_, this->conn_id_, desc_result.handle, sizeof(notify_en),
422 (uint8_t *) &notify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
423 ESP_LOGD(TAG, "Wrote notify descriptor %d, properties=%d", notify_en, char_result.properties);
424 if (status) {
425 ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_write_char_descr error, status=%d", this->connection_index_,
426 this->address_str_.c_str(), status);
427 }
428 break;
429 }
430
431 default:
432 // ideally would check all other events for matching conn_id
433 ESP_LOGD(TAG, "[%d] [%s] Event %d", this->connection_index_, this->address_str_.c_str(), event);
434 break;
435 }
436 return true;
437}
438
439// clients can't call defer() directly since it's protected.
440void BLEClientBase::run_later(std::function<void()> &&f) { // NOLINT
441 this->defer(std::move(f));
442}
443
444void BLEClientBase::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
445 switch (event) {
446 // This event is sent by the server when it requests security
447 case ESP_GAP_BLE_SEC_REQ_EVT:
448 if (!this->check_addr(param->ble_security.auth_cmpl.bd_addr))
449 return;
450 ESP_LOGV(TAG, "[%d] [%s] ESP_GAP_BLE_SEC_REQ_EVT %x", this->connection_index_, this->address_str_.c_str(), event);
451 esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
452 break;
453 // This event is sent once authentication has completed
454 case ESP_GAP_BLE_AUTH_CMPL_EVT:
455 if (!this->check_addr(param->ble_security.auth_cmpl.bd_addr))
456 return;
457 esp_bd_addr_t bd_addr;
458 memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
459 ESP_LOGI(TAG, "[%d] [%s] auth complete. remote BD_ADDR: %s", this->connection_index_, this->address_str_.c_str(),
460 format_hex(bd_addr, 6).c_str());
461 if (!param->ble_security.auth_cmpl.success) {
462 ESP_LOGE(TAG, "[%d] [%s] auth fail reason = 0x%x", this->connection_index_, this->address_str_.c_str(),
463 param->ble_security.auth_cmpl.fail_reason);
464 } else {
465 this->paired_ = true;
466 ESP_LOGD(TAG, "[%d] [%s] auth success. address type = %d auth mode = %d", this->connection_index_,
467 this->address_str_.c_str(), param->ble_security.auth_cmpl.addr_type,
468 param->ble_security.auth_cmpl.auth_mode);
469 }
470 break;
471
472 // There are other events we'll want to implement at some point to support things like pass key
473 // https://github.com/espressif/esp-idf/blob/cba69dd088344ed9d26739f04736ae7a37541b3a/examples/bluetooth/bluedroid/ble/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md
474 default:
475 break;
476 }
477}
478
479// Parse GATT values into a float for a sensor.
480// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
481float BLEClientBase::parse_char_value(uint8_t *value, uint16_t length) {
482 // A length of one means a single octet value.
483 if (length == 0)
484 return 0;
485 if (length == 1)
486 return (float) ((uint8_t) value[0]);
487
488 switch (value[0]) {
489 case 0x1: // boolean.
490 case 0x2: // 2bit.
491 case 0x3: // nibble.
492 case 0x4: // uint8.
493 return (float) ((uint8_t) value[1]);
494 case 0x5: // uint12.
495 case 0x6: // uint16.
496 if (length > 2) {
497 return (float) encode_uint16(value[1], value[2]);
498 }
499 [[fallthrough]];
500 case 0x7: // uint24.
501 if (length > 3) {
502 return (float) encode_uint24(value[1], value[2], value[3]);
503 }
504 [[fallthrough]];
505 case 0x8: // uint32.
506 if (length > 4) {
507 return (float) encode_uint32(value[1], value[2], value[3], value[4]);
508 }
509 [[fallthrough]];
510 case 0xC: // int8.
511 return (float) ((int8_t) value[1]);
512 case 0xD: // int12.
513 case 0xE: // int16.
514 if (length > 2) {
515 return (float) ((int16_t) (value[1] << 8) + (int16_t) value[2]);
516 }
517 [[fallthrough]];
518 case 0xF: // int24.
519 if (length > 3) {
520 return (float) ((int32_t) (value[1] << 16) + (int32_t) (value[2] << 8) + (int32_t) (value[3]));
521 }
522 [[fallthrough]];
523 case 0x10: // int32.
524 if (length > 4) {
525 return (float) ((int32_t) (value[1] << 24) + (int32_t) (value[2] << 16) + (int32_t) (value[3] << 8) +
526 (int32_t) (value[4]));
527 }
528 }
529 ESP_LOGW(TAG, "[%d] [%s] Cannot parse characteristic value of type 0x%x length %d", this->connection_index_,
530 this->address_str_.c_str(), value[0], length);
531 return NAN;
532}
533
535 for (auto *svc : this->services_) {
536 if (svc->uuid == uuid)
537 return svc;
538 }
539 return nullptr;
540}
541
542BLEService *BLEClientBase::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
543
545 auto *svc = this->get_service(service);
546 if (svc == nullptr)
547 return nullptr;
548 return svc->get_characteristic(chr);
549}
550
551BLECharacteristic *BLEClientBase::get_characteristic(uint16_t service, uint16_t chr) {
552 return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr));
553}
554
556 for (auto *svc : this->services_) {
557 if (!svc->parsed)
558 svc->parse_characteristics();
559 for (auto *chr : svc->characteristics) {
560 if (chr->handle == handle)
561 return chr;
562 }
563 }
564 return nullptr;
565}
566
568 auto *chr = this->get_characteristic(handle);
569 if (chr != nullptr) {
570 if (!chr->parsed)
571 chr->parse_descriptors();
572 for (auto &desc : chr->descriptors) {
573 if (desc->uuid.get_uuid().uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG)
574 return desc;
575 }
576 }
577 return nullptr;
578}
579
581 auto *svc = this->get_service(service);
582 if (svc == nullptr)
583 return nullptr;
584 auto *ch = svc->get_characteristic(chr);
585 if (ch == nullptr)
586 return nullptr;
587 return ch->get_descriptor(descr);
588}
589
590BLEDescriptor *BLEClientBase::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) {
591 return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr),
592 espbt::ESPBTUUID::from_uint16(descr));
593}
594
596 for (auto *svc : this->services_) {
597 if (!svc->parsed)
598 svc->parse_characteristics();
599 for (auto *chr : svc->characteristics) {
600 if (!chr->parsed)
601 chr->parse_descriptors();
602 for (auto *desc : chr->descriptors) {
603 if (desc->handle == handle)
604 return desc;
605 }
606 }
607 }
608 return nullptr;
609}
610
611} // namespace esp32_ble_client
612} // namespace esphome
613
614#endif // USE_ESP32
uint8_t status
Definition bl0942.h:8
virtual void mark_failed()
Mark this component as failed.
void enable_loop()
Enable this component's loop.
void disable_loop()
Disable this component's loop.
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
std::vector< BLEService * > services_
BLEDescriptor * get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr)
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override
void set_state(espbt::ClientState st) override
BLECharacteristic * get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr)
void run_later(std::function< void()> &&f)
BLEService * get_service(espbt::ESPBTUUID uuid)
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override
bool parse_device(const espbt::ESPBTDevice &device) override
float parse_char_value(uint8_t *value, uint16_t length)
BLEDescriptor * get_config_descriptor(uint16_t handle)
void print_bt_device_info(const ESPBTDevice &device)
esp_ble_addr_type_t get_address_type() const
ESP32BLETracker * global_esp32_ble_tracker
ESP32BLE * global_ble
Definition ble.cpp:539
const float AFTER_BLUETOOTH
Definition component.cpp:49
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition helpers.cpp:249
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
Definition helpers.h:127
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:131
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:123
uint16_t length
Definition tt21100.cpp:0