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 89355e0879/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
This commit is contained in:
J. Nick Koston 2023-03-26 11:48:56 -10:00 committed by GitHub
parent 36c0e2416d
commit 9ee661c1e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 111 additions and 1 deletions

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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: {

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
}
}
}

View file

@ -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