diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 9fa77d2daa..a8e45e1ea6 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1298,3 +1298,31 @@ message BluetoothConnectionsFreeResponse { uint32 free = 1; uint32 limit = 2; } + +message BluetoothGATTErrorResponse { + option (id) = 82; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; + int32 error = 3; +} + +message BluetoothGATTWriteResponse { + option (id) = 83; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; +} + +message BluetoothGATTNotifyResponse { + option (id) = 84; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + uint32 handle = 2; +} diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 73d8044cde..8cb244f1a1 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -5746,6 +5746,118 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool BluetoothGATTErrorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + case 3: { + this->error = value.as_int32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); + buffer.encode_int32(3, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTErrorResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTErrorResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + + out.append(" error: "); + sprintf(buffer, "%d", this->error); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTWriteResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTWriteResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTWriteResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool BluetoothGATTNotifyResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->handle = value.as_uint32(); + return true; + } + default: + return false; + } +} +void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_uint32(2, this->handle); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothGATTNotifyResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothGATTNotifyResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" handle: "); + sprintf(buffer, "%u", this->handle); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 325e9a23c3..fb676f1cc8 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1481,6 +1481,43 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class BluetoothGATTErrorResponse : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + int32_t error{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTWriteResponse : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothGATTNotifyResponse : public ProtoMessage { + public: + uint64_t address{0}; + uint32_t handle{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 7bfe1acf48..b603ade9de 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -401,6 +401,30 @@ bool APIServerConnectionBase::send_bluetooth_connections_free_response(const Blu return this->send_message_(msg, 81); } #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 82); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 83); +} +#endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 84); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index c7ef1468d8..3cb8b59ba5 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -200,6 +200,15 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 965f08aa15..dbd732f466 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -324,11 +324,21 @@ void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadRespons client->send_bluetooth_gatt_read_response(call); } } +void APIServer::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_write_response(call); + } +} void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) { for (auto &client : this->clients_) { client->send_bluetooth_gatt_notify_data_response(call); } } +void APIServer::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_notify_response(call); + } +} void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) { for (auto &client : this->clients_) { client->send_bluetooth_gatt_get_services_response(call); @@ -342,6 +352,17 @@ void APIServer::send_bluetooth_gatt_services_done(uint64_t address) { client->send_bluetooth_gatt_get_services_done_response(call); } } +void APIServer::send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) { + BluetoothGATTErrorResponse call; + call.address = address; + call.handle = handle; + call.error = error; + + for (auto &client : this->clients_) { + client->send_bluetooth_gatt_error_response(call); + } +} + #endif APIServer::APIServer() { global_api_server = this; } void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 72ab55432c..ec8c54a269 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -78,9 +78,12 @@ class APIServer : public Component, public Controller { void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error = ESP_OK); void send_bluetooth_connections_free(uint8_t free, uint8_t limit); void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); + void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call); void send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call); + void send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call); void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call); void send_bluetooth_gatt_services_done(uint64_t address); + void send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error); #endif void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #ifdef USE_HOMEASSISTANT_TIME diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index b5acea89dd..84841d9bf4 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -4,7 +4,7 @@ import esphome.codegen as cg from esphome.const import CONF_ACTIVE, CONF_ID AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] -DEPENDENCIES = ["esp32"] +DEPENDENCIES = ["api", "esp32"] CODEOWNERS = ["@jesserockz"] diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 96fee39f95..a08c58bd9e 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -4,21 +4,24 @@ #ifdef USE_ESP32 -#ifdef USE_API #include "esphome/components/api/api_server.h" -#endif namespace esphome { namespace bluetooth_proxy { static const char *const TAG = "bluetooth_proxy"; +static const esp_err_t ESP_GATT_NOT_CONNECTED = -1; +static const esp_err_t ESP_GATT_WRONG_ADDRESS = -2; + BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; this->address_ = 0; } bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (!api::global_api_server->is_connected()) + return false; ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(), device.get_rssi()); this->send_api_packet_(device); @@ -38,9 +41,6 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) } void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { -#ifndef USE_API - return; -#else api::BluetoothLEAdvertisementResponse resp; resp.address = device.address_uint64(); if (!device.get_name().empty()) @@ -62,7 +62,6 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi resp.manufacturer_data.push_back(std::move(manufacturer_data)); } api::global_api_server->send_bluetooth_le_advertisement(resp); -#endif } void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, @@ -70,30 +69,23 @@ void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if BLEClientBase::gattc_event_handler(event, gattc_if, param); switch (event) { case ESP_GATTC_DISCONNECT_EVT: { -#ifdef USE_API api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->disconnect.reason); api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->get_bluetooth_connections_limit()); -#endif this->address_ = 0; } case ESP_GATTC_OPEN_EVT: { if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { -#ifdef USE_API api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status); - -#endif break; } break; } case ESP_GATTC_SEARCH_CMPL_EVT: { -#ifdef USE_API api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->get_bluetooth_connections_limit()); -#endif break; } case ESP_GATTC_READ_DESCR_EVT: @@ -101,10 +93,11 @@ void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if if (param->read.conn_id != this->conn_id_) break; if (param->read.status != ESP_GATT_OK) { - ESP_LOGW(TAG, "Error reading char/descriptor at handle %d, status=%d", param->read.handle, param->read.status); + ESP_LOGW(TAG, "Error reading char/descriptor at handle 0x%2X, status=%d", param->read.handle, + param->read.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status); break; } -#ifdef USE_API api::BluetoothGATTReadResponse resp; resp.address = this->address_; resp.handle = param->read.handle; @@ -113,14 +106,56 @@ void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if resp.data.push_back(param->read.value[i]); } api::global_api_server->send_bluetooth_gatt_read_response(resp); -#endif + break; + } + case ESP_GATTC_WRITE_CHAR_EVT: + case ESP_GATTC_WRITE_DESCR_EVT: { + if (param->write.conn_id != this->conn_id_) + break; + if (param->write.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error writing char/descriptor at handle 0x%2X, status=%d", param->write.handle, + param->write.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status); + break; + } + api::BluetoothGATTWriteResponse resp; + resp.address = this->address_; + resp.handle = param->write.handle; + api::global_api_server->send_bluetooth_gatt_write_response(resp); + break; + } + case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { + if (param->unreg_for_notify.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error unregistering notifications for handle 0x%2X, status=%d", param->unreg_for_notify.handle, + param->unreg_for_notify.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle, + param->unreg_for_notify.status); + break; + } + api::BluetoothGATTNotifyResponse resp; + resp.address = this->address_; + resp.handle = param->unreg_for_notify.handle; + api::global_api_server->send_bluetooth_gatt_notify_response(resp); + break; + } + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + if (param->reg_for_notify.status != ESP_GATT_OK) { + ESP_LOGW(TAG, "Error registering notifications for handle 0x%2X, status=%d", param->reg_for_notify.handle, + param->reg_for_notify.status); + api::global_api_server->send_bluetooth_gatt_error(this->address_, param->reg_for_notify.handle, + param->reg_for_notify.status); + break; + } + api::BluetoothGATTNotifyResponse resp; + resp.address = this->address_; + resp.handle = param->reg_for_notify.handle; + api::global_api_server->send_bluetooth_gatt_notify_response(resp); break; } case ESP_GATTC_NOTIFY_EVT: { if (param->notify.conn_id != this->conn_id_) break; - ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%x", param->notify.handle); -#ifdef USE_API + ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%2X", param->notify.handle); api::BluetoothGATTNotifyDataResponse resp; resp.address = this->address_; resp.handle = param->notify.handle; @@ -129,7 +164,6 @@ void BluetoothProxy::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if resp.data.push_back(param->notify.value[i]); } api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); -#endif break; } default: @@ -141,7 +175,6 @@ void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } void BluetoothProxy::loop() { BLEClientBase::loop(); -#ifdef USE_API if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) { ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); @@ -177,10 +210,8 @@ void BluetoothProxy::loop() { api::global_api_server->send_bluetooth_gatt_services(resp); this->send_service_++; } -#endif } -#ifdef USE_API void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { switch (msg.request_type) { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { @@ -220,16 +251,19 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) { if (this->state_ != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); return; } auto *characteristic = this->get_characteristic(msg.handle); if (characteristic == nullptr) { ESP_LOGW(TAG, "Cannot read GATT characteristic, not found."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); return; } @@ -239,43 +273,53 @@ void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &ms esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) { if (this->state_ != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); return; } auto *characteristic = this->get_characteristic(msg.handle); if (characteristic == nullptr) { ESP_LOGW(TAG, "Cannot write GATT characteristic, not found."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); return; } ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str()); - characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(), - msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); + auto err = characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(), + msg.response ? ESP_GATT_WRITE_TYPE_RSP : ESP_GATT_WRITE_TYPE_NO_RSP); + if (err != ERR_OK) { + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); + } } void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) { if (this->state_ != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); return; } auto *descriptor = this->get_descriptor(msg.handle); if (descriptor == nullptr) { ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); return; } @@ -286,22 +330,26 @@ void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTRead esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) { if (this->state_ != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); return; } if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); return; } auto *descriptor = this->get_descriptor(msg.handle); if (descriptor == nullptr) { ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); return; } @@ -313,20 +361,34 @@ void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWri (uint8_t *) msg.data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); if (err != ERR_OK) { ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, err=%d", err); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot get GATT services, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED); + return; + } if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for service list request"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_WRONG_ADDRESS); return; } this->send_service_ = 0; } void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) { + if (this->state_ != espbt::ClientState::ESTABLISHED) { + ESP_LOGW(TAG, "Cannot configure notify, not connected."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); + return; + } + if (this->address_ != msg.address) { ESP_LOGW(TAG, "Address mismatch for notify"); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS); return; } @@ -334,6 +396,7 @@ void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest if (characteristic == nullptr) { ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found."); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_INVALID_HANDLE); return; } @@ -342,17 +405,17 @@ void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, err=%d", err); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } else { err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_ble_gattc_unregister_for_notify failed, err=%d", err); + api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); } } } -#endif - BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace bluetooth_proxy diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index 8ff43aff3f..97047c118b 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -4,6 +4,7 @@ #include +#include "esphome/components/api/api_pb2.h" #include "esphome/components/esp32_ble_client/ble_client_base.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/core/automation.h" @@ -12,10 +13,6 @@ #include -#ifdef USE_API -#include "esphome/components/api/api_pb2.h" -#endif // USE_API - namespace esphome { namespace bluetooth_proxy { @@ -31,7 +28,6 @@ class BluetoothProxy : public BLEClientBase { void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; -#ifdef USE_API void bluetooth_device_request(const api::BluetoothDeviceRequest &msg); void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg); void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg); @@ -39,7 +35,6 @@ class BluetoothProxy : public BLEClientBase { void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg); void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg); -#endif int get_bluetooth_connections_free() { return this->state_ == espbt::ClientState::IDLE ? 1 : 0; } int get_bluetooth_connections_limit() { return 1; } diff --git a/esphome/components/esp32_ble_client/ble_characteristic.cpp b/esphome/components/esp32_ble_client/ble_characteristic.cpp index 873833368c..2692498e98 100644 --- a/esphome/components/esp32_ble_client/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_client/ble_characteristic.cpp @@ -64,17 +64,18 @@ BLEDescriptor *BLECharacteristic::get_descriptor_by_handle(uint16_t handle) { return nullptr; } -void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) { +esp_err_t BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) { auto *client = this->service->client; auto status = esp_ble_gattc_write_char(client->get_gattc_if(), client->get_conn_id(), this->handle, new_val_size, new_val, write_type, ESP_GATT_AUTH_REQ_NONE); if (status) { ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); } + return status; } -void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { - write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP); +esp_err_t BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { + return write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP); } } // namespace esp32_ble_client diff --git a/esphome/components/esp32_ble_client/ble_characteristic.h b/esphome/components/esp32_ble_client/ble_characteristic.h index ffa9227cc4..d290b282fc 100644 --- a/esphome/components/esp32_ble_client/ble_characteristic.h +++ b/esphome/components/esp32_ble_client/ble_characteristic.h @@ -24,8 +24,8 @@ class BLECharacteristic { BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(uint16_t uuid); BLEDescriptor *get_descriptor_by_handle(uint16_t handle); - void write_value(uint8_t *new_val, int16_t new_val_size); - void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type); + esp_err_t write_value(uint8_t *new_val, int16_t new_val_size); + esp_err_t write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type); BLEService *service; }; diff --git a/tests/test1.yaml b/tests/test1.yaml index 01672fcc01..82cdac4623 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -290,8 +290,6 @@ adalight: esp32_ble_tracker: -bluetooth_proxy: - ble_client: - mac_address: AA:BB:CC:DD:EE:FF id: ble_foo diff --git a/tests/test2.yaml b/tests/test2.yaml index 5507fa0631..d485d3f16c 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -506,6 +506,9 @@ xiaomi_ble: mopeka_ble: +bluetooth_proxy: + active: true + xiaomi_rtcgq02lm: - id: motion_rtcgq02lm mac_address: 01:02:03:04:05:06