From 9ee661c1e4efb2bcf11f3490942332d0a5519535 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" <nick@koston.org> Date: Sun, 26 Mar 2023 11:48:56 -1000 Subject: [PATCH] Add ability to clear the gatt cache (#4621) * Add ability to clear the gatt cache With BlueZ we can fully clear the cache when something goes wrong with the services, however since this is also a cache on the ESP32 we need to be able to clear the on device cache as well for the proxies since if something goes wrong with the service resolution it can cache the bad resolution on NVS forever. Our current client implementation is limited to clearing the memory cache in Home Assistant https://github.com/home-assistant/core/blob/89355e087952417a6824507fd3b197f9d0520e19/homeassistant/components/esphome/bluetooth/client.py#L512 related issue https://github.com/esphome/issues/issues/4156 https://github.com/esphome/aioesphomeapi/pull/410 * naming * lint * lint * naming * naming * naming * 88 now that 87 is taken * make const * Update esphome/components/api/api_frame_helper.cpp --- esphome/components/api/api.proto | 11 +++++ esphome/components/api/api_connection.cpp | 4 +- esphome/components/api/api_pb2.cpp | 45 +++++++++++++++++++ esphome/components/api/api_pb2.h | 14 ++++++ esphome/components/api/api_pb2_service.cpp | 8 ++++ esphome/components/api/api_pb2_service.h | 3 ++ esphome/components/api/api_server.cpp | 11 +++++ esphome/components/api/api_server.h | 1 + .../bluetooth_proxy/bluetooth_proxy.cpp | 7 +++ .../bluetooth_proxy/bluetooth_proxy.h | 8 ++++ 10 files changed, 111 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1cebdd0cbe..a64aac52e2 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1156,6 +1156,7 @@ enum BluetoothDeviceRequestType { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5; + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6; } message BluetoothDeviceRequest { @@ -1359,3 +1360,13 @@ message BluetoothDeviceUnpairingResponse { bool success = 2; int32 error = 3; } + +message BluetoothDeviceClearCacheResponse { + option (id) = 88; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + bool success = 2; + int32 error = 3; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 40a5a230a5..e607f45e3f 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -953,7 +953,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.webserver_port = USE_WEBSERVER_PORT; #endif #ifdef USE_BLUETOOTH_PROXY - resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 4 : 1; + resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() + ? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION + : bluetooth_proxy::PASSIVE_ONLY_VERSION; #endif return resp; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 381f8b3c46..43587469af 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -400,6 +400,8 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE"; case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE: return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE"; default: return "UNKNOWN"; } @@ -6060,6 +6062,49 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->success = value.as_bool(); + return true; + } + case 3: { + this->error = value.as_int32(); + return true; + } + default: + return false; + } +} +void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_bool(2, this->success); + buffer.encode_int32(3, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothDeviceClearCacheResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" success: "); + out.append(YESNO(this->success)); + out.append("\n"); + + out.append(" error: "); + sprintf(buffer, "%d", this->error); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index e9025142e9..ff581cac6f 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -163,6 +163,7 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, }; } // namespace enums @@ -1554,6 +1555,19 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class BluetoothDeviceClearCacheResponse : public ProtoMessage { + public: + uint64_t address{0}; + bool success{false}; + 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; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 7ee9e56192..73015fa914 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -441,6 +441,14 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86); } #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str()); +#endif + return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index f1879b2dba..7f19292ff3 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -215,6 +215,9 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index acde0966ba..c60766b364 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -331,6 +331,17 @@ void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success, } } +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; diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 5f92e6b058..db87affdb8 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -80,6 +80,7 @@ class APIServer : public Component, public Controller { 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); diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 55fabf05ef..76950c944e 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -306,6 +306,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret); break; } + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: { + esp_bd_addr_t address; + uint64_to_bd_addr(msg.address, 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); + break; + } } } diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index b99e9a8527..a582abc8a3 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -68,6 +68,14 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com 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 esphome