Send GATT error events to HA (#3884)

This commit is contained in:
Jesse Hills 2022-10-12 22:22:07 +13:00 committed by GitHub
parent fe38b36c26
commit b34d24735a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 334 additions and 40 deletions

View file

@ -1298,3 +1298,31 @@ message BluetoothConnectionsFreeResponse {
uint32 free = 1; uint32 free = 1;
uint32 limit = 2; 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;
}

View file

@ -5746,6 +5746,118 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #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 api
} // namespace esphome } // namespace esphome

View file

@ -1481,6 +1481,43 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
protected: protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; 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 api
} // namespace esphome } // namespace esphome

View file

@ -401,6 +401,30 @@ bool APIServerConnectionBase::send_bluetooth_connections_free_response(const Blu
return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81); return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81);
} }
#endif #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_<BluetoothGATTErrorResponse>(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_<BluetoothGATTWriteResponse>(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_<BluetoothGATTNotifyResponse>(msg, 84);
}
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) { switch (msg_type) {
case 1: { case 1: {

View file

@ -200,6 +200,15 @@ class APIServerConnectionBase : public ProtoService {
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg); 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 #endif
protected: protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;

View file

@ -324,11 +324,21 @@ void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadRespons
client->send_bluetooth_gatt_read_response(call); 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) { void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) {
for (auto &client : this->clients_) { for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_data_response(call); 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) { void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) {
for (auto &client : this->clients_) { for (auto &client : this->clients_) {
client->send_bluetooth_gatt_get_services_response(call); 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); 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 #endif
APIServer::APIServer() { global_api_server = this; } APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute, void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,

View file

@ -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_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_connections_free(uint8_t free, uint8_t limit);
void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); 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_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(const BluetoothGATTGetServicesResponse &call);
void send_bluetooth_gatt_services_done(uint64_t address); 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 #endif
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME

View file

@ -4,7 +4,7 @@ import esphome.codegen as cg
from esphome.const import CONF_ACTIVE, CONF_ID from esphome.const import CONF_ACTIVE, CONF_ID
AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"]
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["api", "esp32"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]

View file

@ -4,21 +4,24 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#ifdef USE_API
#include "esphome/components/api/api_server.h" #include "esphome/components/api/api_server.h"
#endif
namespace esphome { namespace esphome {
namespace bluetooth_proxy { namespace bluetooth_proxy {
static const char *const TAG = "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() { BluetoothProxy::BluetoothProxy() {
global_bluetooth_proxy = this; global_bluetooth_proxy = this;
this->address_ = 0; this->address_ = 0;
} }
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { 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(), ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
device.get_rssi()); device.get_rssi());
this->send_api_packet_(device); 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) { void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
#ifndef USE_API
return;
#else
api::BluetoothLEAdvertisementResponse resp; api::BluetoothLEAdvertisementResponse resp;
resp.address = device.address_uint64(); resp.address = device.address_uint64();
if (!device.get_name().empty()) 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)); resp.manufacturer_data.push_back(std::move(manufacturer_data));
} }
api::global_api_server->send_bluetooth_le_advertisement(resp); 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, 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); BLEClientBase::gattc_event_handler(event, gattc_if, param);
switch (event) { switch (event) {
case ESP_GATTC_DISCONNECT_EVT: { case ESP_GATTC_DISCONNECT_EVT: {
#ifdef USE_API
api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_,
param->disconnect.reason); param->disconnect.reason);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit()); this->get_bluetooth_connections_limit());
#endif
this->address_ = 0; this->address_ = 0;
} }
case ESP_GATTC_OPEN_EVT: { case ESP_GATTC_OPEN_EVT: {
if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN) { 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); api::global_api_server->send_bluetooth_device_connection(this->address_, false, this->mtu_, param->open.status);
#endif
break; break;
} }
break; break;
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { 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_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(),
this->get_bluetooth_connections_limit()); this->get_bluetooth_connections_limit());
#endif
break; break;
} }
case ESP_GATTC_READ_DESCR_EVT: 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_) if (param->read.conn_id != this->conn_id_)
break; break;
if (param->read.status != ESP_GATT_OK) { 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; break;
} }
#ifdef USE_API
api::BluetoothGATTReadResponse resp; api::BluetoothGATTReadResponse resp;
resp.address = this->address_; resp.address = this->address_;
resp.handle = param->read.handle; 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]); resp.data.push_back(param->read.value[i]);
} }
api::global_api_server->send_bluetooth_gatt_read_response(resp); 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; break;
} }
case ESP_GATTC_NOTIFY_EVT: { case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.conn_id != this->conn_id_) if (param->notify.conn_id != this->conn_id_)
break; break;
ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%x", param->notify.handle); ESP_LOGV(TAG, "ESP_GATTC_NOTIFY_EVT: handle=0x%2X", param->notify.handle);
#ifdef USE_API
api::BluetoothGATTNotifyDataResponse resp; api::BluetoothGATTNotifyDataResponse resp;
resp.address = this->address_; resp.address = this->address_;
resp.handle = param->notify.handle; 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]); resp.data.push_back(param->notify.value[i]);
} }
api::global_api_server->send_bluetooth_gatt_notify_data_response(resp); api::global_api_server->send_bluetooth_gatt_notify_data_response(resp);
#endif
break; break;
} }
default: default:
@ -141,7 +175,6 @@ void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); }
void BluetoothProxy::loop() { void BluetoothProxy::loop() {
BLEClientBase::loop(); BLEClientBase::loop();
#ifdef USE_API
if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) { if (this->state_ != espbt::ClientState::IDLE && !api::global_api_server->is_connected()) {
ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str()); ESP_LOGI(TAG, "[%s] Disconnecting.", this->address_str().c_str());
auto err = esp_ble_gattc_close(this->gattc_if_, this->conn_id_); 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); api::global_api_server->send_bluetooth_gatt_services(resp);
this->send_service_++; this->send_service_++;
} }
#endif
} }
#ifdef USE_API
void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) { void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) {
switch (msg.request_type) { switch (msg.request_type) {
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT: { 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) { void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) { if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot read GATT characteristic, not connected."); 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; return;
} }
if (this->address_ != msg.address) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic request"); 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; return;
} }
auto *characteristic = this->get_characteristic(msg.handle); auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) { if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic, not found."); 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; 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); esp_ble_gattc_read_char(this->gattc_if_, this->conn_id_, characteristic->handle, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) { if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); 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) { void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) { if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not connected."); 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; return;
} }
if (this->address_ != msg.address) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic request"); 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; return;
} }
auto *characteristic = this->get_characteristic(msg.handle); auto *characteristic = this->get_characteristic(msg.handle);
if (characteristic == nullptr) { if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic, not found."); 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; return;
} }
ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str()); ESP_LOGV(TAG, "Writing GATT characteristic %s", characteristic->uuid.to_string().c_str());
characteristic->write_value((uint8_t *) msg.data.data(), msg.data.size(), 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); 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) { void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) { if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not connected."); 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; return;
} }
if (this->address_ != msg.address) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for read GATT characteristic descriptor request"); 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; return;
} }
auto *descriptor = this->get_descriptor(msg.handle); auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) { if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT characteristic descriptor, not found."); 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; 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); esp_ble_gattc_read_char_descr(this->gattc_if_, this->conn_id_, descriptor->handle, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) { if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_read_char error, err=%d", err); 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) { void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) {
if (this->state_ != espbt::ClientState::ESTABLISHED) { if (this->state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not connected."); 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; return;
} }
if (this->address_ != msg.address) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for write GATT characteristic descriptor request"); 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; return;
} }
auto *descriptor = this->get_descriptor(msg.handle); auto *descriptor = this->get_descriptor(msg.handle);
if (descriptor == nullptr) { if (descriptor == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT characteristic descriptor, not found."); 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; 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); (uint8_t *) msg.data.data(), ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (err != ERR_OK) { if (err != ERR_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, err=%d", err); 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) { 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) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for service list request"); 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; return;
} }
this->send_service_ = 0; this->send_service_ = 0;
} }
void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) { 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) { if (this->address_ != msg.address) {
ESP_LOGW(TAG, "Address mismatch for notify"); ESP_LOGW(TAG, "Address mismatch for notify");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_WRONG_ADDRESS);
return; return;
} }
@ -334,6 +396,7 @@ void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest
if (characteristic == nullptr) { if (characteristic == nullptr) {
ESP_LOGW(TAG, "Cannot notify GATT characteristic, not found."); 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; 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); err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, err=%d", err); 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 { } else {
err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle); err = esp_ble_gattc_unregister_for_notify(this->gattc_if_, this->remote_bda_, characteristic->handle);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_gattc_unregister_for_notify failed, err=%d", err); 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) BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace bluetooth_proxy } // namespace bluetooth_proxy

View file

@ -4,6 +4,7 @@
#include <map> #include <map>
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/esp32_ble_client/ble_client_base.h" #include "esphome/components/esp32_ble_client/ble_client_base.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
@ -12,10 +13,6 @@
#include <map> #include <map>
#ifdef USE_API
#include "esphome/components/api/api_pb2.h"
#endif // USE_API
namespace esphome { namespace esphome {
namespace bluetooth_proxy { 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, void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;
#ifdef USE_API
void bluetooth_device_request(const api::BluetoothDeviceRequest &msg); void bluetooth_device_request(const api::BluetoothDeviceRequest &msg);
void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg); void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg);
void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &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_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg);
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg);
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &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_free() { return this->state_ == espbt::ClientState::IDLE ? 1 : 0; }
int get_bluetooth_connections_limit() { return 1; } int get_bluetooth_connections_limit() { return 1; }

View file

@ -64,17 +64,18 @@ BLEDescriptor *BLECharacteristic::get_descriptor_by_handle(uint16_t handle) {
return nullptr; 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 *client = this->service->client;
auto status = esp_ble_gattc_write_char(client->get_gattc_if(), client->get_conn_id(), this->handle, new_val_size, 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); new_val, write_type, ESP_GATT_AUTH_REQ_NONE);
if (status) { if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", 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) { esp_err_t 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); return write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
} }
} // namespace esp32_ble_client } // namespace esp32_ble_client

View file

@ -24,8 +24,8 @@ class BLECharacteristic {
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid); BLEDescriptor *get_descriptor(uint16_t uuid);
BLEDescriptor *get_descriptor_by_handle(uint16_t handle); BLEDescriptor *get_descriptor_by_handle(uint16_t handle);
void 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);
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_gatt_write_type_t write_type);
BLEService *service; BLEService *service;
}; };

View file

@ -290,8 +290,6 @@ adalight:
esp32_ble_tracker: esp32_ble_tracker:
bluetooth_proxy:
ble_client: ble_client:
- mac_address: AA:BB:CC:DD:EE:FF - mac_address: AA:BB:CC:DD:EE:FF
id: ble_foo id: ble_foo

View file

@ -506,6 +506,9 @@ xiaomi_ble:
mopeka_ble: mopeka_ble:
bluetooth_proxy:
active: true
xiaomi_rtcgq02lm: xiaomi_rtcgq02lm:
- id: motion_rtcgq02lm - id: motion_rtcgq02lm
mac_address: 01:02:03:04:05:06 mac_address: 01:02:03:04:05:06