Bluetooth Proxy: Raw bundled advertisements (#4924)

This commit is contained in:
Jesse Hills 2023-06-09 07:41:09 +12:00 committed by GitHub
parent 1cf210fa25
commit ce13979690
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 450 additions and 235 deletions

View file

@ -206,7 +206,8 @@ message DeviceInfoResponse {
uint32 webserver_port = 10; uint32 webserver_port = 10;
uint32 bluetooth_proxy_version = 11; uint32 legacy_bluetooth_proxy_version = 11;
uint32 bluetooth_proxy_feature_flags = 15;
string manufacturer = 12; string manufacturer = 12;
@ -1130,6 +1131,8 @@ message SubscribeBluetoothLEAdvertisementsRequest {
option (id) = 66; option (id) = 66;
option (source) = SOURCE_CLIENT; option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY"; option (ifdef) = "USE_BLUETOOTH_PROXY";
uint32 flags = 1;
} }
message BluetoothServiceData { message BluetoothServiceData {
@ -1154,6 +1157,23 @@ message BluetoothLEAdvertisementResponse {
uint32 address_type = 7; uint32 address_type = 7;
} }
message BluetoothLERawAdvertisement {
uint64 address = 1;
sint32 rssi = 2;
uint32 address_type = 3;
bytes data = 4;
}
message BluetoothLERawAdvertisementsResponse {
option (id) = 93;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
option (no_delay) = true;
repeated BluetoothLERawAdvertisement advertisements = 1;
}
enum BluetoothDeviceRequestType { enum BluetoothDeviceRequestType {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0;
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1; BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;

View file

@ -51,6 +51,14 @@ void APIConnection::start() {
helper_->set_log_info(client_info_); helper_->set_log_info(client_info_);
} }
APIConnection::~APIConnection() {
#ifdef USE_BLUETOOTH_PROXY
if (bluetooth_proxy::global_bluetooth_proxy->get_api_connection() == this) {
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
}
#endif
}
void APIConnection::loop() { void APIConnection::loop() {
if (this->remove_) if (this->remove_)
return; return;
@ -845,9 +853,13 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
}
void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
}
bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) { bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
if (!this->bluetooth_le_advertisement_subscription_)
return false;
if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) { if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
BluetoothLEAdvertisementResponse resp = msg; BluetoothLEAdvertisementResponse resp = msg;
for (auto &service : resp.service_data) { for (auto &service : resp.service_data) {
@ -943,7 +955,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp; HelloResponse resp;
resp.api_version_major = 1; resp.api_version_major = 1;
resp.api_version_minor = 8; resp.api_version_minor = 9;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name(); resp.name = App.get_name();
@ -995,9 +1007,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.webserver_port = USE_WEBSERVER_PORT; resp.webserver_port = USE_WEBSERVER_PORT;
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() resp.legacy_bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->get_legacy_version();
? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
: bluetooth_proxy::PASSIVE_ONLY_VERSION;
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version(); resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();

View file

@ -16,7 +16,7 @@ namespace api {
class APIConnection : public APIServerConnection { class APIConnection : public APIServerConnection {
public: public:
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent); APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
virtual ~APIConnection() = default; virtual ~APIConnection();
void start(); void start();
void loop(); void loop();
@ -98,12 +98,8 @@ class APIConnection : public APIServerConnection {
this->send_homeassistant_service_response(call); this->send_homeassistant_service_response(call);
} }
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override { void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
this->bluetooth_le_advertisement_subscription_ = true; void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
}
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override {
this->bluetooth_le_advertisement_subscription_ = false;
}
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg); bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override; void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
@ -211,9 +207,6 @@ class APIConnection : public APIServerConnection {
uint32_t last_traffic_; uint32_t last_traffic_;
bool sent_ping_{false}; bool sent_ping_{false};
bool service_call_subscription_{false}; bool service_call_subscription_{false};
#ifdef USE_BLUETOOTH_PROXY
bool bluetooth_le_advertisement_subscription_{false};
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool voice_assistant_subscription_{false}; bool voice_assistant_subscription_{false};
#endif #endif

View file

@ -617,7 +617,11 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
return true; return true;
} }
case 11: { case 11: {
this->bluetooth_proxy_version = value.as_uint32(); this->legacy_bluetooth_proxy_version = value.as_uint32();
return true;
}
case 15: {
this->bluetooth_proxy_feature_flags = value.as_uint32();
return true; return true;
} }
case 14: { case 14: {
@ -681,7 +685,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(8, this->project_name); buffer.encode_string(8, this->project_name);
buffer.encode_string(9, this->project_version); buffer.encode_string(9, this->project_version);
buffer.encode_uint32(10, this->webserver_port); buffer.encode_uint32(10, this->webserver_port);
buffer.encode_uint32(11, this->bluetooth_proxy_version); buffer.encode_uint32(11, this->legacy_bluetooth_proxy_version);
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
buffer.encode_string(12, this->manufacturer); buffer.encode_string(12, this->manufacturer);
buffer.encode_string(13, this->friendly_name); buffer.encode_string(13, this->friendly_name);
buffer.encode_uint32(14, this->voice_assistant_version); buffer.encode_uint32(14, this->voice_assistant_version);
@ -731,8 +736,13 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" bluetooth_proxy_version: "); out.append(" legacy_bluetooth_proxy_version: ");
sprintf(buffer, "%u", this->bluetooth_proxy_version); sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version);
out.append(buffer);
out.append("\n");
out.append(" bluetooth_proxy_feature_flags: ");
sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5041,10 +5051,28 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->flags = value.as_uint32();
return true;
}
default:
return false;
}
}
void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->flags);
}
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
out.append("SubscribeBluetoothLEAdvertisementsRequest {}"); __attribute__((unused)) char buffer[64];
out.append("SubscribeBluetoothLEAdvertisementsRequest {\n");
out.append(" flags: ");
sprintf(buffer, "%u", this->flags);
out.append(buffer);
out.append("\n");
out.append("}");
} }
#endif #endif
bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -5197,6 +5225,92 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
bool BluetoothLERawAdvertisement::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->address = value.as_uint64();
return true;
}
case 2: {
this->rssi = value.as_sint32();
return true;
}
case 3: {
this->address_type = value.as_uint32();
return true;
}
default:
return false;
}
}
bool BluetoothLERawAdvertisement::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4: {
this->data = value.as_string();
return true;
}
default:
return false;
}
}
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_sint32(2, this->rssi);
buffer.encode_uint32(3, this->address_type);
buffer.encode_string(4, this->data);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothLERawAdvertisement {\n");
out.append(" address: ");
sprintf(buffer, "%llu", this->address);
out.append(buffer);
out.append("\n");
out.append(" rssi: ");
sprintf(buffer, "%d", this->rssi);
out.append(buffer);
out.append("\n");
out.append(" address_type: ");
sprintf(buffer, "%u", this->address_type);
out.append(buffer);
out.append("\n");
out.append(" data: ");
out.append("'").append(this->data).append("'");
out.append("\n");
out.append("}");
}
#endif
bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->advertisements.push_back(value.as_message<BluetoothLERawAdvertisement>());
return true;
}
default:
return false;
}
}
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->advertisements) {
buffer.encode_message<BluetoothLERawAdvertisement>(1, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothLERawAdvertisementsResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothLERawAdvertisementsResponse {\n");
for (const auto &it : this->advertisements) {
out.append(" advertisements: ");
it.dump_to(out);
out.append("\n");
}
out.append("}");
}
#endif
bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {

View file

@ -287,7 +287,8 @@ class DeviceInfoResponse : public ProtoMessage {
std::string project_name{}; std::string project_name{};
std::string project_version{}; std::string project_version{};
uint32_t webserver_port{0}; uint32_t webserver_port{0};
uint32_t bluetooth_proxy_version{0}; uint32_t legacy_bluetooth_proxy_version{0};
uint32_t bluetooth_proxy_feature_flags{0};
std::string manufacturer{}; std::string manufacturer{};
std::string friendly_name{}; std::string friendly_name{};
uint32_t voice_assistant_version{0}; uint32_t voice_assistant_version{0};
@ -1247,12 +1248,14 @@ class MediaPlayerCommandRequest : public ProtoMessage {
}; };
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public: public:
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
#endif #endif
protected: protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class BluetoothServiceData : public ProtoMessage { class BluetoothServiceData : public ProtoMessage {
public: public:
@ -1286,6 +1289,32 @@ class BluetoothLEAdvertisementResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class BluetoothLERawAdvertisement : public ProtoMessage {
public:
uint64_t address{0};
int32_t rssi{0};
uint32_t address_type{0};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
public:
std::vector<BluetoothLERawAdvertisement> advertisements{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class BluetoothDeviceRequest : public ProtoMessage { class BluetoothDeviceRequest : public ProtoMessage {
public: public:
uint64_t address{0}; uint64_t address{0};

View file

@ -339,6 +339,15 @@ bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const Blu
} }
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response(
const BluetoothLERawAdvertisementsResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLERawAdvertisementsResponse>(msg, 93);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif #endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) { bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {

View file

@ -161,6 +161,9 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
#endif #endif

View file

@ -291,112 +291,7 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
client->send_homeassistant_service_call(call); client->send_homeassistant_service_call(call);
} }
} }
#ifdef USE_BLUETOOTH_PROXY
void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_le_advertisement(call);
}
}
void APIServer::send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
BluetoothDeviceConnectionResponse call;
call.address = address;
call.connected = connected;
call.mtu = mtu;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_connection_response(call);
}
}
void APIServer::send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error) {
BluetoothDevicePairingResponse call;
call.address = address;
call.paired = paired;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_pairing_response(call);
}
}
void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error) {
BluetoothDeviceUnpairingResponse call;
call.address = address;
call.success = success;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_unpairing_response(call);
}
}
void APIServer::send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error) {
BluetoothDeviceClearCacheResponse call;
call.address = address;
call.success = success;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_clear_cache_response(call);
}
}
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
BluetoothConnectionsFreeResponse call;
call.free = free;
call.limit = limit;
for (auto &client : this->clients_) {
client->send_bluetooth_connections_free_response(call);
}
}
void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call) {
for (auto &client : this->clients_) {
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);
}
}
void APIServer::send_bluetooth_gatt_services_done(uint64_t address) {
BluetoothGATTGetServicesDoneResponse call;
call.address = address;
for (auto &client : this->clients_) {
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; } 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,
std::function<void(std::string)> f) { std::function<void(std::string)> f) {

View file

@ -75,21 +75,6 @@ class APIServer : public Component, public Controller {
void on_media_player_update(media_player::MediaPlayer *obj) override; void on_media_player_update(media_player::MediaPlayer *obj) override;
#endif #endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
#ifdef USE_BLUETOOTH_PROXY
void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call);
void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
void send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK);
void send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK);
void send_bluetooth_device_clear_cache(uint64_t address, bool success, 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); } void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
void request_time(); void request_time();

View file

@ -1,6 +1,6 @@
#include "bluetooth_connection.h" #include "bluetooth_connection.h"
#include "esphome/components/api/api_server.h" #include "esphome/components/api/api_pb2.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -20,24 +20,21 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
switch (event) { switch (event) {
case ESP_GATTC_DISCONNECT_EVT: { case ESP_GATTC_DISCONNECT_EVT: {
api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->disconnect.reason); this->proxy_->send_device_connection(this->address_, false, 0, param->disconnect.reason);
this->set_address(0); this->set_address(0);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->send_connections_free();
this->proxy_->get_bluetooth_connections_limit());
break; break;
} }
case ESP_GATTC_OPEN_EVT: { case ESP_GATTC_OPEN_EVT: {
if (param->open.conn_id != this->conn_id_) if (param->open.conn_id != this->conn_id_)
break; break;
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) {
api::global_api_server->send_bluetooth_device_connection(this->address_, false, 0, param->open.status); this->proxy_->send_device_connection(this->address_, false, 0, param->open.status);
this->set_address(0); this->set_address(0);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->send_connections_free();
this->proxy_->get_bluetooth_connections_limit());
} else if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) { } else if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); this->proxy_->send_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->send_connections_free();
this->proxy_->get_bluetooth_connections_limit());
} }
this->seen_mtu_or_services_ = false; this->seen_mtu_or_services_ = false;
break; break;
@ -52,9 +49,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
this->seen_mtu_or_services_ = true; this->seen_mtu_or_services_ = true;
break; break;
} }
api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); this->proxy_->send_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->send_connections_free();
this->proxy_->get_bluetooth_connections_limit());
break; break;
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
@ -67,9 +63,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
this->seen_mtu_or_services_ = true; this->seen_mtu_or_services_ = true;
break; break;
} }
api::global_api_server->send_bluetooth_device_connection(this->address_, true, this->mtu_); this->proxy_->send_device_connection(this->address_, true, this->mtu_);
api::global_api_server->send_bluetooth_connections_free(this->proxy_->get_bluetooth_connections_free(), this->proxy_->send_connections_free();
this->proxy_->get_bluetooth_connections_limit());
break; break;
} }
case ESP_GATTC_READ_DESCR_EVT: case ESP_GATTC_READ_DESCR_EVT:
@ -79,7 +74,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
if (param->read.status != ESP_GATT_OK) { if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_, ESP_LOGW(TAG, "[%d] [%s] Error reading char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
this->address_str_.c_str(), param->read.handle, param->read.status); this->address_str_.c_str(), param->read.handle, param->read.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->read.handle, param->read.status); this->proxy_->send_gatt_error(this->address_, param->read.handle, param->read.status);
break; break;
} }
api::BluetoothGATTReadResponse resp; api::BluetoothGATTReadResponse resp;
@ -89,7 +84,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
for (uint16_t i = 0; i < param->read.value_len; i++) { for (uint16_t i = 0; i < param->read.value_len; i++) {
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); this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp);
break; break;
} }
case ESP_GATTC_WRITE_CHAR_EVT: case ESP_GATTC_WRITE_CHAR_EVT:
@ -99,13 +94,13 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
if (param->write.status != ESP_GATT_OK) { if (param->write.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_, ESP_LOGW(TAG, "[%d] [%s] Error writing char/descriptor at handle 0x%2X, status=%d", this->connection_index_,
this->address_str_.c_str(), param->write.handle, param->write.status); this->address_str_.c_str(), param->write.handle, param->write.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->write.handle, param->write.status); this->proxy_->send_gatt_error(this->address_, param->write.handle, param->write.status);
break; break;
} }
api::BluetoothGATTWriteResponse resp; api::BluetoothGATTWriteResponse resp;
resp.address = this->address_; resp.address = this->address_;
resp.handle = param->write.handle; resp.handle = param->write.handle;
api::global_api_server->send_bluetooth_gatt_write_response(resp); this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp);
break; break;
} }
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: { case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
@ -113,28 +108,26 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
ESP_LOGW(TAG, "[%d] [%s] Error unregistering notifications for handle 0x%2X, status=%d", ESP_LOGW(TAG, "[%d] [%s] Error unregistering notifications for handle 0x%2X, status=%d",
this->connection_index_, this->address_str_.c_str(), param->unreg_for_notify.handle, this->connection_index_, this->address_str_.c_str(), param->unreg_for_notify.handle,
param->unreg_for_notify.status); param->unreg_for_notify.status);
api::global_api_server->send_bluetooth_gatt_error(this->address_, param->unreg_for_notify.handle, this->proxy_->send_gatt_error(this->address_, param->unreg_for_notify.handle, param->unreg_for_notify.status);
param->unreg_for_notify.status);
break; break;
} }
api::BluetoothGATTNotifyResponse resp; api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_; resp.address = this->address_;
resp.handle = param->unreg_for_notify.handle; resp.handle = param->unreg_for_notify.handle;
api::global_api_server->send_bluetooth_gatt_notify_response(resp); this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
if (param->reg_for_notify.status != ESP_GATT_OK) { if (param->reg_for_notify.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "[%d] [%s] Error registering notifications for handle 0x%2X, status=%d", this->connection_index_, ESP_LOGW(TAG, "[%d] [%s] Error registering notifications for handle 0x%2X, status=%d", this->connection_index_,
this->address_str_.c_str(), param->reg_for_notify.handle, param->reg_for_notify.status); this->address_str_.c_str(), 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, this->proxy_->send_gatt_error(this->address_, param->reg_for_notify.handle, param->reg_for_notify.status);
param->reg_for_notify.status);
break; break;
} }
api::BluetoothGATTNotifyResponse resp; api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_; resp.address = this->address_;
resp.handle = param->reg_for_notify.handle; resp.handle = param->reg_for_notify.handle;
api::global_api_server->send_bluetooth_gatt_notify_response(resp); this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
break; break;
} }
case ESP_GATTC_NOTIFY_EVT: { case ESP_GATTC_NOTIFY_EVT: {
@ -149,7 +142,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
for (uint16_t i = 0; i < param->notify.value_len; i++) { for (uint16_t i = 0; i < param->notify.value_len; i++) {
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); this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp);
break; break;
} }
default: default:
@ -166,10 +159,9 @@ void BluetoothConnection::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl
if (memcmp(param->ble_security.auth_cmpl.bd_addr, this->remote_bda_, 6) != 0) if (memcmp(param->ble_security.auth_cmpl.bd_addr, this->remote_bda_, 6) != 0)
break; break;
if (param->ble_security.auth_cmpl.success) { if (param->ble_security.auth_cmpl.success) {
api::global_api_server->send_bluetooth_device_pairing(this->address_, true); this->proxy_->send_device_pairing(this->address_, true);
} else { } else {
api::global_api_server->send_bluetooth_device_pairing(this->address_, false, this->proxy_->send_device_pairing(this->address_, false, param->ble_security.auth_cmpl.fail_reason);
param->ble_security.auth_cmpl.fail_reason);
} }
break; break;
default: default:

View file

@ -1,11 +1,10 @@
#include "bluetooth_proxy.h" #include "bluetooth_proxy.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/macros.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include "esphome/components/api/api_server.h"
namespace esphome { namespace esphome {
namespace bluetooth_proxy { namespace bluetooth_proxy {
@ -27,15 +26,39 @@ std::vector<uint64_t> get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) {
BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; } BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; }
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()) if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_)
return false; 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);
return true; return true;
} }
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
return false;
api::BluetoothLERawAdvertisementsResponse resp;
for (size_t i = 0; i < count; i++) {
auto &result = advertisements[i];
api::BluetoothLERawAdvertisement adv;
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
adv.rssi = result.rssi;
adv.address_type = result.ble_addr_type;
uint8_t length = result.adv_data_len + result.scan_rsp_len;
adv.data.reserve(length);
for (uint16_t i = 0; i < length; i++) {
adv.data.push_back(result.ble_adv[i]);
}
resp.advertisements.push_back(std::move(adv));
}
ESP_LOGV(TAG, "Proxying %d packets", count);
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
return true;
}
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
api::BluetoothLEAdvertisementResponse resp; api::BluetoothLEAdvertisementResponse resp;
resp.address = device.address_uint64(); resp.address = device.address_uint64();
@ -58,7 +81,7 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
manufacturer_data.data.assign(data.data.begin(), data.data.end()); manufacturer_data.data.assign(data.data.begin(), data.data.end());
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); this->api_connection_->send_bluetooth_le_advertisement(resp);
} }
void BluetoothProxy::dump_config() { void BluetoothProxy::dump_config() {
@ -81,7 +104,7 @@ int BluetoothProxy::get_bluetooth_connections_free() {
} }
void BluetoothProxy::loop() { void BluetoothProxy::loop() {
if (!api::global_api_server->is_connected()) { if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr) {
for (auto *connection : this->connections_) { for (auto *connection : this->connections_) {
if (connection->get_address() != 0) { if (connection->get_address() != 0) {
connection->disconnect(); connection->disconnect();
@ -92,7 +115,7 @@ void BluetoothProxy::loop() {
for (auto *connection : this->connections_) { for (auto *connection : this->connections_) {
if (connection->send_service_ == connection->service_count_) { if (connection->send_service_ == connection->service_count_) {
connection->send_service_ = DONE_SENDING_SERVICES; connection->send_service_ = DONE_SENDING_SERVICES;
api::global_api_server->send_bluetooth_gatt_services_done(connection->get_address()); this->send_gatt_services_done(connection->get_address());
if (connection->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE || if (connection->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
connection->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { connection->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
connection->release_services(); connection->release_services();
@ -170,7 +193,7 @@ void BluetoothProxy::loop() {
service_resp.characteristics.push_back(std::move(characteristic_resp)); service_resp.characteristics.push_back(std::move(characteristic_resp));
} }
resp.services.push_back(std::move(service_resp)); resp.services.push_back(std::move(service_resp));
api::global_api_server->send_bluetooth_gatt_services(resp); this->api_connection_->send_bluetooth_gatt_get_services_response(resp);
} }
} }
} }
@ -208,16 +231,15 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
auto *connection = this->get_connection_(msg.address, true); auto *connection = this->get_connection_(msg.address, true);
if (connection == nullptr) { if (connection == nullptr) {
ESP_LOGW(TAG, "No free connections available"); ESP_LOGW(TAG, "No free connections available");
api::global_api_server->send_bluetooth_device_connection(msg.address, false); this->send_device_connection(msg.address, false);
return; return;
} }
if (connection->state() == espbt::ClientState::CONNECTED || if (connection->state() == espbt::ClientState::CONNECTED ||
connection->state() == espbt::ClientState::ESTABLISHED) { connection->state() == espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%d] [%s] Connection already established", connection->get_connection_index(), ESP_LOGW(TAG, "[%d] [%s] Connection already established", connection->get_connection_index(),
connection->address_str().c_str()); connection->address_str().c_str());
api::global_api_server->send_bluetooth_device_connection(msg.address, true); this->send_device_connection(msg.address, true);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->send_connections_free();
this->get_bluetooth_connections_limit());
return; return;
} else if (connection->state() == espbt::ClientState::SEARCHING) { } else if (connection->state() == espbt::ClientState::SEARCHING) {
ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already searching for device", ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, already searching for device",
@ -263,25 +285,22 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
} else { } else {
connection->set_state(espbt::ClientState::SEARCHING); connection->set_state(espbt::ClientState::SEARCHING);
} }
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->send_connections_free();
this->get_bluetooth_connections_limit());
break; break;
} }
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT: {
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
api::global_api_server->send_bluetooth_device_connection(msg.address, false); this->send_device_connection(msg.address, false);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->send_connections_free();
this->get_bluetooth_connections_limit());
return; return;
} }
if (connection->state() != espbt::ClientState::IDLE) { if (connection->state() != espbt::ClientState::IDLE) {
connection->disconnect(); connection->disconnect();
} else { } else {
connection->set_address(0); connection->set_address(0);
api::global_api_server->send_bluetooth_device_connection(msg.address, false); this->send_device_connection(msg.address, false);
api::global_api_server->send_bluetooth_connections_free(this->get_bluetooth_connections_free(), this->send_connections_free();
this->get_bluetooth_connections_limit());
} }
break; break;
} }
@ -291,10 +310,10 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
if (!connection->is_paired()) { if (!connection->is_paired()) {
auto err = connection->pair(); auto err = connection->pair();
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_device_pairing(msg.address, false, err); this->send_device_pairing(msg.address, false, err);
} }
} else { } else {
api::global_api_server->send_bluetooth_device_pairing(msg.address, true); this->send_device_pairing(msg.address, true);
} }
} }
break; break;
@ -303,14 +322,20 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
esp_bd_addr_t address; esp_bd_addr_t address;
uint64_to_bd_addr(msg.address, address); uint64_to_bd_addr(msg.address, address);
esp_err_t ret = esp_ble_remove_bond_device(address); esp_err_t ret = esp_ble_remove_bond_device(address);
api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret); this->send_device_pairing(msg.address, ret == ESP_OK, ret);
break; break;
} }
case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: { case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: {
esp_bd_addr_t address; esp_bd_addr_t address;
uint64_to_bd_addr(msg.address, address); uint64_to_bd_addr(msg.address, address);
esp_err_t ret = esp_ble_gattc_cache_clean(address); esp_err_t ret = esp_ble_gattc_cache_clean(address);
api::global_api_server->send_bluetooth_device_clear_cache(msg.address, ret == ESP_OK, ret); api::BluetoothDeviceClearCacheResponse call;
call.address = msg.address;
call.success = ret == ESP_OK;
call.error = ret;
this->api_connection_->send_bluetooth_device_clear_cache_response(call);
break; break;
} }
} }
@ -320,13 +345,13 @@ void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &ms
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
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); this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return; return;
} }
auto err = connection->read_characteristic(msg.handle); auto err = connection->read_characteristic(msg.handle);
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); this->send_gatt_error(msg.address, msg.handle, err);
} }
} }
@ -334,13 +359,13 @@ void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
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); this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return; return;
} }
auto err = connection->write_characteristic(msg.handle, msg.data, msg.response); auto err = connection->write_characteristic(msg.handle, msg.data, msg.response);
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); this->send_gatt_error(msg.address, msg.handle, err);
} }
} }
@ -348,13 +373,13 @@ void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTRead
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
ESP_LOGW(TAG, "Cannot read GATT descriptor, not connected"); ESP_LOGW(TAG, "Cannot read GATT descriptor, not connected");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return; return;
} }
auto err = connection->read_descriptor(msg.handle); auto err = connection->read_descriptor(msg.handle);
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); this->send_gatt_error(msg.address, msg.handle, err);
} }
} }
@ -362,13 +387,13 @@ void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWri
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
ESP_LOGW(TAG, "Cannot write GATT descriptor, not connected"); ESP_LOGW(TAG, "Cannot write GATT descriptor, not connected");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return; return;
} }
auto err = connection->write_descriptor(msg.handle, msg.data, true); auto err = connection->write_descriptor(msg.handle, msg.data, true);
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); this->send_gatt_error(msg.address, msg.handle, err);
} }
} }
@ -376,12 +401,12 @@ void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetSer
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr || !connection->connected()) { if (connection == nullptr || !connection->connected()) {
ESP_LOGW(TAG, "Cannot get GATT services, not connected"); ESP_LOGW(TAG, "Cannot get GATT services, not connected");
api::global_api_server->send_bluetooth_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED); this->send_gatt_error(msg.address, 0, ESP_GATT_NOT_CONNECTED);
return; return;
} }
if (!connection->service_count_) { if (!connection->service_count_) {
ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str()); ESP_LOGW(TAG, "[%d] [%s] No GATT services found", connection->connection_index_, connection->address_str().c_str());
api::global_api_server->send_bluetooth_gatt_services_done(msg.address); this->send_gatt_services_done(msg.address);
return; return;
} }
if (connection->send_service_ == if (connection->send_service_ ==
@ -393,16 +418,89 @@ void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest
auto *connection = this->get_connection_(msg.address, false); auto *connection = this->get_connection_(msg.address, false);
if (connection == nullptr) { if (connection == nullptr) {
ESP_LOGW(TAG, "Cannot notify GATT characteristic, not connected"); ESP_LOGW(TAG, "Cannot notify GATT characteristic, not connected");
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED); this->send_gatt_error(msg.address, msg.handle, ESP_GATT_NOT_CONNECTED);
return; return;
} }
auto err = connection->notify_characteristic(msg.handle, msg.enable); auto err = connection->notify_characteristic(msg.handle, msg.enable);
if (err != ESP_OK) { if (err != ESP_OK) {
api::global_api_server->send_bluetooth_gatt_error(msg.address, msg.handle, err); this->send_gatt_error(msg.address, msg.handle, err);
} }
} }
void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags) {
if (this->api_connection_ != nullptr) {
ESP_LOGE(TAG, "Only one API subscription is allowed at a time");
return;
}
this->api_connection_ = api_connection;
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
}
void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
if (this->api_connection_ != api_connection) {
ESP_LOGV(TAG, "API connection is not subscribed");
return;
}
this->api_connection_ = nullptr;
this->raw_advertisements_ = false;
}
void BluetoothProxy::send_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
if (this->api_connection_ == nullptr)
return;
api::BluetoothDeviceConnectionResponse call;
call.address = address;
call.connected = connected;
call.mtu = mtu;
call.error = error;
this->api_connection_->send_bluetooth_device_connection_response(call);
}
void BluetoothProxy::send_connections_free() {
if (this->api_connection_ == nullptr)
return;
api::BluetoothConnectionsFreeResponse call;
call.free = this->get_bluetooth_connections_free();
call.limit = this->get_bluetooth_connections_limit();
this->api_connection_->send_bluetooth_connections_free_response(call);
}
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
if (this->api_connection_ == nullptr)
return;
api::BluetoothGATTGetServicesDoneResponse call;
call.address = address;
this->api_connection_->send_bluetooth_gatt_get_services_done_response(call);
}
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
if (this->api_connection_ == nullptr)
return;
api::BluetoothGATTErrorResponse call;
call.address = address;
call.handle = handle;
call.error = error;
this->api_connection_->send_bluetooth_gatt_error_response(call);
}
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
api::BluetoothDevicePairingResponse call;
call.address = address;
call.paired = paired;
call.error = error;
this->api_connection_->send_bluetooth_device_pairing_response(call);
}
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
api::BluetoothDeviceUnpairingResponse call;
call.address = address;
call.success = success;
call.error = error;
this->api_connection_->send_bluetooth_device_unpairing_response(call);
}
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

@ -5,6 +5,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include "esphome/components/api/api_connection.h"
#include "esphome/components/api/api_pb2.h" #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"
@ -21,10 +22,33 @@ static const esp_err_t ESP_GATT_NOT_CONNECTED = -1;
using namespace esp32_ble_client; using namespace esp32_ble_client;
// Legacy versions:
// Version 1: Initial version without active connections
// Version 2: Support for active connections
// Version 3: New connection API
// Version 4: Pairing support
// Version 5: Cache clear support
static const uint32_t LEGACY_ACTIVE_CONNECTIONS_VERSION = 5;
static const uint32_t LEGACY_PASSIVE_ONLY_VERSION = 1;
enum BluetoothProxyFeature : uint32_t {
FEATURE_PASSIVE_SCAN = 1 << 0,
FEATURE_ACTIVE_CONNECTIONS = 1 << 1,
FEATURE_REMOTE_CACHING = 1 << 2,
FEATURE_PAIRING = 1 << 3,
FEATURE_CACHE_CLEARING = 1 << 4,
FEATURE_RAW_ADVERTISEMENTS = 1 << 5,
};
enum BluetoothProxySubscriptionFlag : uint32_t {
SUBSCRIPTION_RAW_ADVERTISEMENTS = 1 << 0,
};
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component { class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public: public:
BluetoothProxy(); BluetoothProxy();
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
void dump_config() override; void dump_config() override;
void loop() override; void loop() override;
@ -44,6 +68,18 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
int get_bluetooth_connections_free(); int get_bluetooth_connections_free();
int get_bluetooth_connections_limit() { return this->connections_.size(); } int get_bluetooth_connections_limit() { return this->connections_.size(); }
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags);
void unsubscribe_api_connection(api::APIConnection *api_connection);
api::APIConnection *get_api_connection() { return this->api_connection_; }
void send_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
void send_connections_free();
void send_gatt_services_done(uint64_t address);
void send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error);
void send_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK);
void send_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK);
void send_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK);
static void uint64_to_bd_addr(uint64_t address, esp_bd_addr_t bd_addr) { static void uint64_to_bd_addr(uint64_t address, esp_bd_addr_t bd_addr) {
bd_addr[0] = (address >> 40) & 0xff; bd_addr[0] = (address >> 40) & 0xff;
bd_addr[1] = (address >> 32) & 0xff; bd_addr[1] = (address >> 32) & 0xff;
@ -56,6 +92,27 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
void set_active(bool active) { this->active_ = active; } void set_active(bool active) { this->active_ = active; }
bool has_active() { return this->active_; } bool has_active() { return this->active_; }
uint32_t get_legacy_version() const {
if (this->active_) {
return LEGACY_ACTIVE_CONNECTIONS_VERSION;
}
return LEGACY_PASSIVE_ONLY_VERSION;
}
uint32_t get_feature_flags() const {
uint32_t flags = 0;
flags |= BluetoothProxyFeature::FEATURE_PASSIVE_SCAN;
flags |= BluetoothProxyFeature::FEATURE_RAW_ADVERTISEMENTS;
if (this->active_) {
flags |= BluetoothProxyFeature::FEATURE_ACTIVE_CONNECTIONS;
flags |= BluetoothProxyFeature::FEATURE_REMOTE_CACHING;
flags |= BluetoothProxyFeature::FEATURE_PAIRING;
flags |= BluetoothProxyFeature::FEATURE_CACHE_CLEARING;
}
return flags;
}
protected: protected:
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
@ -64,18 +121,12 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
bool active_; bool active_;
std::vector<BluetoothConnection *> connections_{}; std::vector<BluetoothConnection *> connections_{};
api::APIConnection *api_connection_{nullptr};
bool raw_advertisements_{false};
}; };
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
// Version 1: Initial version without active connections
// Version 2: Support for active connections
// Version 3: New connection API
// Version 4: Pairing support
// Version 5: Cache clear support
static const uint32_t ACTIVE_CONNECTIONS_VERSION = 5;
static const uint32_t PASSIVE_ONLY_VERSION = 1;
} // namespace bluetooth_proxy } // namespace bluetooth_proxy
} // namespace esphome } // namespace esphome

View file

@ -244,6 +244,17 @@ void ESP32BLE::dump_config() {
} }
} }
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
uint64_t u = 0;
u |= uint64_t(address[0] & 0xFF) << 40;
u |= uint64_t(address[1] & 0xFF) << 32;
u |= uint64_t(address[2] & 0xFF) << 24;
u |= uint64_t(address[3] & 0xFF) << 16;
u |= uint64_t(address[4] & 0xFF) << 8;
u |= uint64_t(address[5] & 0xFF) << 0;
return u;
}
ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) ESP32BLE *global_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esp32_ble } // namespace esp32_ble

View file

@ -18,6 +18,8 @@
namespace esphome { namespace esphome {
namespace esp32_ble { namespace esp32_ble {
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
// NOLINTNEXTLINE(modernize-use-using) // NOLINTNEXTLINE(modernize-use-using)
typedef struct { typedef struct {
void *peer_device; void *peer_device;

View file

@ -34,17 +34,6 @@ static const char *const TAG = "esp32_ble_tracker";
ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
uint64_t u = 0;
u |= uint64_t(address[0] & 0xFF) << 40;
u |= uint64_t(address[1] & 0xFF) << 32;
u |= uint64_t(address[2] & 0xFF) << 24;
u |= uint64_t(address[3] & 0xFF) << 16;
u |= uint64_t(address[4] & 0xFF) << 8;
u |= uint64_t(address[5] & 0xFF) << 0;
return u;
}
float ESP32BLETracker::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } float ESP32BLETracker::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void ESP32BLETracker::setup() { void ESP32BLETracker::setup() {
@ -114,10 +103,20 @@ void ESP32BLETracker::loop() {
if (this->scan_result_index_ && // if it looks like we have a scan result we will take the lock if (this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) { xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) {
uint32_t index = this->scan_result_index_; uint32_t index = this->scan_result_index_;
if (index) {
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up."); ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
} }
bool bulk_parsed = false;
for (auto *listener : this->listeners_) {
bulk_parsed |= listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
}
for (auto *client : this->clients_) {
bulk_parsed |= client->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
}
if (!bulk_parsed) {
for (size_t i = 0; i < index; i++) { for (size_t i = 0; i < index; i++) {
ESPBTDevice device; ESPBTDevice device;
device.parse_scan_rst(this->scan_result_buffer_[i]); device.parse_scan_rst(this->scan_result_buffer_[i]);
@ -141,8 +140,8 @@ void ESP32BLETracker::loop() {
this->print_bt_device_info(device); this->print_bt_device_info(device);
} }
} }
this->scan_result_index_ = 0;
} }
this->scan_result_index_ = 0;
xSemaphoreGive(this->scan_result_lock_); xSemaphoreGive(this->scan_result_lock_);
} }
@ -585,7 +584,7 @@ std::string ESPBTDevice::address_str() const {
this->address_[3], this->address_[4], this->address_[5]); this->address_[3], this->address_[4], this->address_[5]);
return mac; return mac;
} }
uint64_t ESPBTDevice::address_uint64() const { return ble_addr_to_uint64(this->address_); } uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
void ESP32BLETracker::dump_config() { void ESP32BLETracker::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Tracker:"); ESP_LOGCONFIG(TAG, "BLE Tracker:");

View file

@ -113,6 +113,9 @@ class ESPBTDeviceListener {
public: public:
virtual void on_scan_end() {} virtual void on_scan_end() {}
virtual bool parse_device(const ESPBTDevice &device) = 0; virtual bool parse_device(const ESPBTDevice &device) = 0;
virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
return false;
};
void set_parent(ESP32BLETracker *parent) { parent_ = parent; } void set_parent(ESP32BLETracker *parent) { parent_ = parent; }
protected: protected: