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