diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 88af5957e72..f1bc9b003ae 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1760,7 +1760,7 @@ enum BluetoothDeviceRequestType { message BluetoothDeviceRequest { option (id) = 68; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; BluetoothDeviceRequestType request_type = 2; @@ -1771,7 +1771,7 @@ message BluetoothDeviceRequest { message BluetoothDeviceConnectionResponse { option (id) = 69; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; bool connected = 2; @@ -1782,7 +1782,7 @@ message BluetoothDeviceConnectionResponse { message BluetoothGATTGetServicesRequest { option (id) = 70; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; } @@ -1826,7 +1826,7 @@ message BluetoothGATTService { message BluetoothGATTGetServicesResponse { option (id) = 71; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; repeated BluetoothGATTService services = 2; @@ -1835,7 +1835,7 @@ message BluetoothGATTGetServicesResponse { message BluetoothGATTGetServicesDoneResponse { option (id) = 72; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; } @@ -1843,7 +1843,7 @@ message BluetoothGATTGetServicesDoneResponse { message BluetoothGATTReadRequest { option (id) = 73; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1852,7 +1852,7 @@ message BluetoothGATTReadRequest { message BluetoothGATTReadResponse { option (id) = 74; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1864,7 +1864,7 @@ message BluetoothGATTReadResponse { message BluetoothGATTWriteRequest { option (id) = 75; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1876,7 +1876,7 @@ message BluetoothGATTWriteRequest { message BluetoothGATTReadDescriptorRequest { option (id) = 76; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1885,7 +1885,7 @@ message BluetoothGATTReadDescriptorRequest { message BluetoothGATTWriteDescriptorRequest { option (id) = 77; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1896,7 +1896,7 @@ message BluetoothGATTWriteDescriptorRequest { message BluetoothGATTNotifyRequest { option (id) = 78; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1906,7 +1906,7 @@ message BluetoothGATTNotifyRequest { message BluetoothGATTNotifyDataResponse { option (id) = 79; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1917,13 +1917,13 @@ message BluetoothGATTNotifyDataResponse { message SubscribeBluetoothConnectionsFreeRequest { option (id) = 80; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; } message BluetoothConnectionsFreeResponse { option (id) = 81; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint32 free = 1; uint32 limit = 2; @@ -1936,7 +1936,7 @@ message BluetoothConnectionsFreeResponse { message BluetoothGATTErrorResponse { option (id) = 82; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1946,7 +1946,7 @@ message BluetoothGATTErrorResponse { message BluetoothGATTWriteResponse { option (id) = 83; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1955,7 +1955,7 @@ message BluetoothGATTWriteResponse { message BluetoothGATTNotifyResponse { option (id) = 84; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 handle = 2; @@ -1964,7 +1964,7 @@ message BluetoothGATTNotifyResponse { message BluetoothDevicePairingResponse { option (id) = 85; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; bool paired = 2; @@ -1974,7 +1974,7 @@ message BluetoothDevicePairingResponse { message BluetoothDeviceUnpairingResponse { option (id) = 86; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; bool success = 2; @@ -1990,7 +1990,7 @@ message UnsubscribeBluetoothLEAdvertisementsRequest { message BluetoothDeviceClearCacheResponse { option (id) = 88; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; bool success = 2; @@ -2807,7 +2807,7 @@ message SerialProxyRequestResponse { message BluetoothSetConnectionParamsRequest { option (id) = 145; option (source) = SOURCE_CLIENT; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; uint32 min_interval = 2; // units of 1.25ms @@ -2819,7 +2819,7 @@ message BluetoothSetConnectionParamsRequest { message BluetoothSetConnectionParamsResponse { option (id) = 146; option (source) = SOURCE_SERVER; - option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (ifdef) = "USE_BLUETOOTH_PROXY_CONNECTIONS"; uint64 address = 1; int32 error = 2; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 19d2b14a321..afd7e696afc 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1236,6 +1236,7 @@ void APIConnection::on_subscribe_bluetooth_le_advertisements_request( void APIConnection::on_unsubscribe_bluetooth_le_advertisements_request() { bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this); } +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void APIConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) { bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg); } @@ -1269,13 +1270,15 @@ void APIConnection::on_subscribe_bluetooth_connections_free_request() { } } +void APIConnection::on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &msg) { + bluetooth_proxy::global_bluetooth_proxy->bluetooth_set_connection_params(msg); +} +#endif + void APIConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) { bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode( msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE); } -void APIConnection::on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &msg) { - bluetooth_proxy::global_bluetooth_proxy->bluetooth_set_connection_params(msg); -} #endif #ifdef USE_VOICE_ASSISTANT diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 9ca1b8b6a46..d548b921b39 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -183,6 +183,7 @@ class APIConnection final : public APIServerConnectionBase { void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg); void on_unsubscribe_bluetooth_le_advertisements_request(); +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_device_request(const BluetoothDeviceRequest &msg); void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg); void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg); @@ -191,8 +192,9 @@ class APIConnection final : public APIServerConnectionBase { void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg); void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg); void on_subscribe_bluetooth_connections_free_request(); - void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg); void on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &msg); +#endif + void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg); #endif #ifdef USE_HOMEASSISTANT_TIME @@ -390,7 +392,7 @@ class APIConnection final : public APIServerConnectionBase { #ifdef USE_API_NOISE bool send_noise_encryption_set_key_response_(const NoiseEncryptionSetKeyRequest &msg); #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS bool send_subscribe_bluetooth_connections_free_response_(); #endif #ifdef USE_VOICE_ASSISTANT diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 5776ec5c62d..1b8c6b05bd7 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -2482,6 +2482,8 @@ BluetoothLERawAdvertisementsResponse::calculate_size() const { } return size; } +#endif +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, proto_varint_value_t value) { switch (field_id) { case 1: @@ -2858,6 +2860,8 @@ uint32_t BluetoothDeviceClearCacheResponse::calculate_size() const { size += ProtoSize::calc_int32(1, this->error); return size; } +#endif +#ifdef USE_BLUETOOTH_PROXY uint8_t *BluetoothScannerStateResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const { uint8_t *__restrict__ pos = buffer.get_pos(); ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, static_cast(this->state)); @@ -4221,7 +4225,7 @@ uint32_t SerialProxyRequestResponse::calculate_size() const { return size; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS bool BluetoothSetConnectionParamsRequest::decode_varint(uint32_t field_id, proto_varint_value_t value) { switch (field_id) { case 1: diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index f35f5510603..8335dae1f24 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -225,7 +225,7 @@ enum MediaPlayerFormatPurpose : uint32_t { MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1, }; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0, BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1, @@ -235,6 +235,8 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, }; +#endif +#ifdef USE_BLUETOOTH_PROXY enum BluetoothScannerState : uint32_t { BLUETOOTH_SCANNER_STATE_IDLE = 0, BLUETOOTH_SCANNER_STATE_STARTING = 1, @@ -1999,6 +2001,8 @@ class BluetoothLERawAdvertisementsResponse final : public ProtoMessage { protected: }; +#endif +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS class BluetoothDeviceRequest final : public ProtoDecodableMessage { public: static constexpr uint8_t MESSAGE_TYPE = 68; @@ -2384,6 +2388,8 @@ class BluetoothDeviceClearCacheResponse final : public ProtoMessage { protected: }; +#endif +#ifdef USE_BLUETOOTH_PROXY class BluetoothScannerStateResponse final : public ProtoMessage { public: static constexpr uint8_t MESSAGE_TYPE = 126; @@ -3358,7 +3364,7 @@ class SerialProxyRequestResponse final : public ProtoMessage { protected: }; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS class BluetoothSetConnectionParamsRequest final : public ProtoDecodableMessage { public: static constexpr uint8_t MESSAGE_TYPE = 145; diff --git a/esphome/components/api/api_pb2_defines.h b/esphome/components/api/api_pb2_defines.h index 8ebd60fb5d1..3603fac6d77 100644 --- a/esphome/components/api/api_pb2_defines.h +++ b/esphome/components/api/api_pb2_defines.h @@ -3,7 +3,7 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_BLUETOOTH_PROXY +#if defined(USE_BLUETOOTH_PROXY) || defined(USE_BLUETOOTH_PROXY_CONNECTIONS) #ifndef USE_API_VARINT64 #define USE_API_VARINT64 #endif diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index 17ce7fba45b..4d5829e45de 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -584,7 +584,7 @@ template<> const char *proto_enum_to_string(enu } } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS template<> const char *proto_enum_to_string(enums::BluetoothDeviceRequestType value) { switch (value) { @@ -606,6 +606,8 @@ const char *proto_enum_to_string(enums::Bluet return ESPHOME_PSTR("UNKNOWN"); } } +#endif +#ifdef USE_BLUETOOTH_PROXY template<> const char *proto_enum_to_string(enums::BluetoothScannerState value) { switch (value) { case enums::BLUETOOTH_SCANNER_STATE_IDLE: @@ -2002,6 +2004,8 @@ const char *BluetoothLERawAdvertisementsResponse::dump_to(DumpBuffer &out) const } return out.c_str(); } +#endif +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS const char *BluetoothDeviceRequest::dump_to(DumpBuffer &out) const { MessageDumpHelper helper(out, ESPHOME_PSTR("BluetoothDeviceRequest")); dump_field(out, ESPHOME_PSTR("address"), this->address); @@ -2173,6 +2177,8 @@ const char *BluetoothDeviceClearCacheResponse::dump_to(DumpBuffer &out) const { dump_field(out, ESPHOME_PSTR("error"), this->error); return out.c_str(); } +#endif +#ifdef USE_BLUETOOTH_PROXY const char *BluetoothScannerStateResponse::dump_to(DumpBuffer &out) const { MessageDumpHelper helper(out, ESPHOME_PSTR("BluetoothScannerStateResponse")); dump_field(out, ESPHOME_PSTR("state"), static_cast(this->state)); @@ -2764,7 +2770,7 @@ const char *SerialProxyRequestResponse::dump_to(DumpBuffer &out) const { return out.c_str(); } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS const char *BluetoothSetConnectionParamsRequest::dump_to(DumpBuffer &out) const { MessageDumpHelper helper(out, ESPHOME_PSTR("BluetoothSetConnectionParamsRequest")); dump_field(out, ESPHOME_PSTR("address"), this->address); diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 19dcbfb77cb..65c7b8858cc 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -302,7 +302,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothDeviceRequest::MESSAGE_TYPE: { BluetoothDeviceRequest msg; msg.decode(msg_data, msg_size); @@ -313,7 +313,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTGetServicesRequest::MESSAGE_TYPE: { BluetoothGATTGetServicesRequest msg; msg.decode(msg_data, msg_size); @@ -324,7 +324,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTReadRequest::MESSAGE_TYPE: { BluetoothGATTReadRequest msg; msg.decode(msg_data, msg_size); @@ -335,7 +335,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTWriteRequest::MESSAGE_TYPE: { BluetoothGATTWriteRequest msg; msg.decode(msg_data, msg_size); @@ -346,7 +346,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTReadDescriptorRequest::MESSAGE_TYPE: { BluetoothGATTReadDescriptorRequest msg; msg.decode(msg_data, msg_size); @@ -357,7 +357,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTWriteDescriptorRequest::MESSAGE_TYPE: { BluetoothGATTWriteDescriptorRequest msg; msg.decode(msg_data, msg_size); @@ -368,7 +368,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothGATTNotifyRequest::MESSAGE_TYPE: { BluetoothGATTNotifyRequest msg; msg.decode(msg_data, msg_size); @@ -379,7 +379,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case 80 /* SubscribeBluetoothConnectionsFreeRequest is empty */: { #ifdef HAS_PROTO_MESSAGE_DUMP this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request")); @@ -694,7 +694,7 @@ void APIConnection::read_message_(uint32_t msg_size, uint32_t msg_type, const ui break; } #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS case BluetoothSetConnectionParamsRequest::MESSAGE_TYPE: { BluetoothSetConnectionParamsRequest msg; msg.decode(msg_data, msg_size); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 5ed78b3385c..6abdf7093e1 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -115,32 +115,32 @@ class APIServerConnectionBase { void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_device_request(const BluetoothDeviceRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_subscribe_bluetooth_connections_free_request(){}; #endif @@ -235,7 +235,7 @@ class APIServerConnectionBase { void on_serial_proxy_request(const SerialProxyRequest &value){}; #endif -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &value){}; #endif }; diff --git a/esphome/components/bluetooth_connection/bluetooth_connection.cpp b/esphome/components/bluetooth_connection/bluetooth_connection.cpp index a7e9825e56d..a001729083c 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection.cpp @@ -5,7 +5,7 @@ #include #endif -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS #include "esphome/components/api/api_pb2.h" #include "esphome/core/log.h" @@ -44,7 +44,7 @@ BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size } // namespace esphome::bluetooth_connection -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS #if defined(USE_ESP32) && defined(USE_BLE_GATT_CLIENT) namespace esphome::bluetooth_connection { diff --git a/esphome/components/bluetooth_connection/bluetooth_connection.h b/esphome/components/bluetooth_connection/bluetooth_connection.h index bcfbdaa6cf6..b21d997b4fc 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection.h @@ -16,16 +16,14 @@ #include #endif -// The connection-aware API request handlers are compiled: a GATT backend is -// wired by codegen (one slot per connection). This is the single spelling of -// that predicate - the hub wrapper and the API request handlers gate on it. -// The wrapper serves the proxy's API surface, so it compiles only when a -// backend AND the proxy are present. The address-scoped maintenance functions -// below are only reached from that gated surface; their #else stubs just -// keep this header parsing on arms without a backend. -#if defined(USE_BLE_GATT_CLIENT) && defined(USE_BLUETOOTH_PROXY) -#define BLUETOOTH_CONNECTION_HAS_GATT -#endif +// USE_BLUETOOTH_PROXY_CONNECTIONS is the single spelling of "this build has +// proxy connection slots": codegen emits it per configured slot, and each +// slot brings a GATT backend, so it also implies USE_BLE_GATT_CLIENT (not +// the converse: a backend can exist without proxy slots). The hub +// wrapper, the proxy's connection surface and the API's connection messages +// all gate on it. The address-scoped maintenance functions below are only +// reached from that gated surface; the #else stubs just keep this header +// parsing on arms without a backend. namespace esphome::api { class BluetoothGATTGetServicesResponse; @@ -154,7 +152,7 @@ inline void fill_gatt_uuid(std::array &uuid_128, uint32_t &short_uu } } -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS /// Result of close_service_batch: keep filling the batch or send it now. /// An oversized service is packed alone; a failed (backpressured) send is /// retried from the batch start, so no service is silently skipped. @@ -166,6 +164,6 @@ enum class BatchClose : uint8_t { CONTINUE, SEND }; /// cannot drift. BatchClose close_service_batch(api::BluetoothGATTGetServicesResponse &resp, size_t ¤t_size, int16_t &send_service, uint8_t connection_index, const char *address_str); -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS } // namespace esphome::bluetooth_connection diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp index 99a6a312ecb..076c77b18e1 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp @@ -4,7 +4,7 @@ // The in-place streamer serves the proxy's service-discovery API; backend-only // builds compile without the proxy headers or the streamer. -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS #include "bluetooth_connection.h" #include "bluetooth_connection_hub.h" @@ -391,7 +391,7 @@ void BluedroidGattClient::deliver_pending_search_() { this->listener_->on_service_discovery_done(this->search_status_); } -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // The wrapper's compile-time streamer detection must keep finding this // method; a signature drift would silently fall back to the table streamer, // which proxy builds compile without a materializer. @@ -535,7 +535,7 @@ void BluedroidGattClient::stream_service_batch(BluetoothConnection &conn) { } conn.batch_stalled_ = false; } -#endif // USE_BLUETOOTH_PROXY +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS // ---- events ---- diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h index 19b89ea5cd7..0d0b4fed5b6 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.h @@ -20,7 +20,7 @@ namespace esphome::bluetooth_connection { -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS class BluetoothConnection; #endif @@ -79,7 +79,7 @@ class BluedroidGattClient final : public esp32_ble_tracker::ESPBTClient, public ble_device_base::GattServiceTable get_service_table() { return {}; } void release_services(); -#ifdef USE_BLUETOOTH_PROXY +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS /// In-place service streamer (the proxy wrapper detects and prefers it): /// builds one api response batch directly from Bluedroid's cached database, /// so the streaming peak is the response itself - the old esp32 model. diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp index 8707637e9d9..50267e7c731 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_hub.cpp @@ -21,7 +21,7 @@ // request; timeouts, disconnects and errors raise instead of caching. #include "bluetooth_connection_hub.h" -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS #include "esphome/components/api/api_pb2.h" #include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" @@ -560,4 +560,4 @@ void BluetoothConnection::send_service_for_discovery_() { } // namespace esphome::bluetooth_connection -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_hub.h b/esphome/components/bluetooth_connection/bluetooth_connection_hub.h index 3553f8bf00a..f87d545f7df 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_hub.h +++ b/esphome/components/bluetooth_connection/bluetooth_connection_hub.h @@ -10,7 +10,7 @@ // The wrapper exists to serve the proxy's API surface; direct consumers // drive the backend themselves, so backend-only builds compile this header // empty. -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS #include "esphome/components/ble_device_base/ble_client_state.h" #include "bluetooth_connection_gatt_backend.h" @@ -271,4 +271,4 @@ static_assert(sizeof(void *) != 4 || sizeof(BluetoothConnection) <= 56, } // namespace esphome::bluetooth_connection -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index cc7aed6be23..95f71fc8eab 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -208,6 +208,11 @@ async def _connections_to_code(var: cg.MockObj, config: ConfigType) -> None: # this define whenever a proxy is present (zero on advertisement-only # hubs); sized here so it can never diverge from the loop below. cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", len(connections)) + if connections: + # Gates the connection and GATT half of the API surface. A proxy + # without slots omits FEATURE_ACTIVE_CONNECTIONS, so a client never + # sends those requests and their handlers and encoders are dead. + cg.add_define("USE_BLUETOOTH_PROXY_CONNECTIONS") for connection_conf in connections: backend = await bluetooth_connection.new_gatt_backend(connection_conf) connection = cg.new_Pvariable(connection_conf[CONF_ID]) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 0cf8483cea8..75830e83ef9 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -70,9 +70,10 @@ void BluetoothProxy::send_polled_scanner_state_() { #endif // USE_BLE_SCANNER_STATE_CALLBACK void BluetoothProxy::setup() { - // BLUETOOTH_PROXY_MAX_CONNECTIONS is 0 on an advertisement-only proxy. +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS this->connections_free_response_.limit = BLUETOOTH_PROXY_MAX_CONNECTIONS; this->connections_free_response_.free = BLUETOOTH_PROXY_MAX_CONNECTIONS; +#endif // Capture the configured scan mode from YAML before any API changes this->configured_scan_active_ = this->hub_->scan_active(); @@ -111,7 +112,7 @@ void BluetoothProxy::on_raw_advertisement_(const ble_device_base::RawAdvertiseme } } -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void BluetoothProxy::log_connection_request_ignored_(BluetoothConnection *connection, ClientState state) { ESP_LOGW(TAG, "[%d] [%s] Connection request ignored, state: %s", connection->get_connection_index(), connection->address_str(), ble_device_base::client_state_to_string(state)); @@ -120,8 +121,9 @@ void BluetoothProxy::log_connection_request_ignored_(BluetoothConnection *connec void BluetoothProxy::log_connection_info_(BluetoothConnection *connection, const char *message) { ESP_LOGI(TAG, "[%d] [%s] Connecting %s", connection->get_connection_index(), connection->address_str(), message); } -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void BluetoothProxy::log_reply_dropped_(const char *what, uint64_t address) { ESP_LOGW(TAG, "%s reply for %012" PRIX64 " dropped, TCP buffer full", what, address); } @@ -146,6 +148,7 @@ void BluetoothProxy::handle_gatt_not_connected_(uint64_t address, uint16_t handl this->log_reply_dropped_("Not-connected", address); } } +#endif void BluetoothProxy::log_advertisement_flush_() { ESP_LOGV(TAG, "Sent batch of %u BLE advertisements", this->response_.advertisements_len); @@ -159,7 +162,7 @@ void BluetoothProxy::dump_config() { this->get_bluetooth_mac_address_pretty(mac_str); const char *mac_out = mac_str[0] != '\0' ? mac_str : "unavailable (adapter not up yet)"; const char *scan_mode = this->configured_scan_active_ ? "active" : "passive"; -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS ESP_LOGCONFIG(TAG, "Bluetooth Proxy:\n" " Active: %s\n" @@ -177,12 +180,9 @@ void BluetoothProxy::dump_config() { #endif } -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS -// maybe_unused: in a passive proxy (active: false) MAX is 0, the body is removed, and connection is unused. -void BluetoothProxy::register_connection([[maybe_unused]] BluetoothConnection *connection) { -// Guard the always-false comparison (-Wtype-limits) in a passive proxy (active: false), where MAX is 0. -#if BLUETOOTH_PROXY_MAX_CONNECTIONS > 0 +void BluetoothProxy::register_connection(BluetoothConnection *connection) { if (this->connection_count_ >= BLUETOOTH_PROXY_MAX_CONNECTIONS) { // Cannot happen with codegen-sized registration; a silent drop would // surface later as a null proxy_ dereference, so refuse loudly. @@ -193,7 +193,6 @@ void BluetoothProxy::register_connection([[maybe_unused]] BluetoothConnection *c connection->connection_index_ = this->connection_count_; this->connections_[this->connection_count_++] = connection; connection->proxy_ = this; -#endif } void BluetoothProxy::log_slot_accounting_mismatch_() { ESP_LOGW(TAG, "Connection slot free-count mismatch, clamped"); } @@ -549,7 +548,7 @@ void BluetoothProxy::bluetooth_set_connection_params(const api::BluetoothSetConn } } -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS #ifdef USE_ESP32 @@ -592,7 +591,7 @@ void BluetoothProxy::bluetooth_scanner_set_mode(bool active) { #endif // USE_ESP32 void BluetoothProxy::loop() { -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // Stream pending service-discovery batches every iteration; the streamer // handles a vanished API connection itself. for (uint8_t i = 0; i < this->connection_count_; i++) { @@ -606,6 +605,7 @@ void BluetoothProxy::loop() { return; this->last_advertisement_flush_time_ = now; +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS if (this->connections_free_pending_ && this->api_connection_ != nullptr) { // Resend a dropped slot-state update, paced by the 100 ms gate so the // retry does not hammer the congestion it exists to survive. Every build @@ -614,9 +614,10 @@ void BluetoothProxy::loop() { this->connections_free_pending_ = false; this->send_connections_free(this->api_connection_); } +#endif if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr) { -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // The API subscriber is gone: tear down any connections it left behind // (disconnect() on an already-disconnecting slot is a no-op). for (uint8_t i = 0; i < this->connection_count_; i++) { @@ -629,7 +630,7 @@ void BluetoothProxy::loop() { return; } -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // Paced retries of owed per-slot notifications; subscriber swaps clear // stale latches before this runs. for (uint8_t i = 0; i < this->connection_count_; i++) { @@ -670,28 +671,10 @@ void BluetoothProxy::loop() { this->flush_pending_advertisements_(); } -#ifndef BLUETOOTH_CONNECTION_HAS_GATT - -// Advertisement-only proxy: no connection backend on this platform, or -// active: false. get_feature_flags() then omits FEATURE_ACTIVE_CONNECTIONS, -// so a client treats the proxy as passive and never sends a connection or -// GATT request. These exist only because the api layer dispatches them -// unconditionally; answering would link response encoders this build has no -// use for. - -void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_read_descriptor(const api::BluetoothGATTReadDescriptorRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg) {} -void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg) {} -void BluetoothProxy::bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg) {} - -#endif // !BLUETOOTH_CONNECTION_HAS_GATT - void BluetoothProxy::reset_owed_replies_() { +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS this->connections_free_pending_ = false; +#endif #ifdef USE_BLE_SCANNER_STATE_CALLBACK // Owed on unsubscribe; on subscribe the trailing send_scanner_state_() // re-drives it from the hub, so clearing it there is free. @@ -703,7 +686,7 @@ void BluetoothProxy::reset_owed_replies_() { // detector runs, and a re-subscribe re-arms this anyway. this->last_scan_running_ = !this->hub_->scan_running(); #endif -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS this->pending_unpairing_.clear(); this->pending_disconnections_.fill({}); for (uint8_t i = 0; i < this->connection_count_; i++) { @@ -752,6 +735,7 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti this->reset_owed_replies_(); } +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void BluetoothProxy::send_connections_free() { if (this->api_connection_ != nullptr) { this->send_connections_free(this->api_connection_); @@ -797,7 +781,6 @@ bool BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, conn_err return this->api_connection_->send_message(call); } -#ifdef BLUETOOTH_CONNECTION_HAS_GATT void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, conn_err_t error) { if (this->api_connection_ == nullptr) return; @@ -859,7 +842,7 @@ void BluetoothProxy::send_device_clear_cache(uint64_t address, bool success, con this->log_reply_dropped_("Clear-cache", address); } } -#endif +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index de70b35aafb..e5f3a259a11 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -29,7 +29,7 @@ using bluetooth_connection::DONE_SENDING_SERVICES; using bluetooth_connection::INIT_SENDING_SERVICES; using bluetooth_connection::SERVICES_DONE_PENDING; -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS using BluetoothConnection = bluetooth_connection::BluetoothConnection; using ClientState = ble_device_base::ClientState; #endif @@ -60,7 +60,7 @@ enum BluetoothProxySubscriptionFlag : uint32_t { SUBSCRIPTION_RAW_ADVERTISEMENTS = 1 << 0, }; -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS /// One owed address-keyed reply in a single word: 48-bit address low, 16-bit /// error on top. Every error that reaches it fits int16_t. class PendingReply { @@ -98,7 +98,7 @@ static_assert(PendingReply{}.empty()); #endif class BluetoothProxy final : public Component { -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // Allow the connection to update connections_free_response_ friend bluetooth_connection::BluetoothConnection; #endif @@ -109,9 +109,9 @@ class BluetoothProxy final : public Component { void setup() override; void loop() override; -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void register_connection(BluetoothConnection *connection); -#endif // BLUETOOTH_CONNECTION_HAS_GATT +#endif // USE_BLUETOOTH_PROXY_CONNECTIONS #ifndef USE_ESP32 // Run after the hub's setup() (the trackers use AFTER_WIFI): setup() below // snapshots scan_active()/scan_running() and installs the raw callback, and @@ -120,6 +120,7 @@ class BluetoothProxy final : public Component { float get_setup_priority() const override { return setup_priority::AFTER_WIFI - 1.0f; } #endif // !USE_ESP32 +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS void bluetooth_device_request(const api::BluetoothDeviceRequest &msg); void bluetooth_gatt_read(const api::BluetoothGATTReadRequest &msg); void bluetooth_gatt_write(const api::BluetoothGATTWriteRequest &msg); @@ -128,6 +129,7 @@ class BluetoothProxy final : public Component { void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg); void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg); void bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg); +#endif void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags); void unsubscribe_api_connection(api::APIConnection *api_connection); @@ -137,6 +139,7 @@ class BluetoothProxy final : public Component { return this->api_connection_ != nullptr && this->api_connection_->client_supports_api_version(1, 12); } +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS /// False only when a subscriber refused the frame; true = delivered or /// nobody subscribed. Refusals latch in send_device_disconnected_() and /// send_connected_reply_(); other callers report via log_reply_dropped_(). @@ -147,7 +150,6 @@ class BluetoothProxy final : public Component { bool send_gatt_services_done(uint64_t address); /// False only when the API refused the frame, so the reply is still owed. bool send_gatt_error(uint64_t address, uint16_t handle, conn_err_t error); -#ifdef BLUETOOTH_CONNECTION_HAS_GATT void send_device_pairing(uint64_t address, bool paired, conn_err_t error = CONN_OK); /// No default error: the drain rebuilds success as (error == CONN_OK), so a /// caller that omitted it would have a reported failure resent as a success. @@ -243,22 +245,17 @@ class BluetoothProxy final : public Component { } void log_advertisement_flush_(); -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS BluetoothConnection *get_connection_(uint64_t address, bool reserve); void log_connection_request_ignored_(BluetoothConnection *connection, ClientState state); void log_connection_info_(BluetoothConnection *connection, const char *message); -#endif void log_not_connected_gatt_(const char *action, const char *type); void handle_gatt_not_connected_(uint64_t address, uint16_t handle, const char *action, const char *type); -#ifdef BLUETOOTH_CONNECTION_HAS_GATT /// Keep the pre-allocated connections-free message in step when a /// connection slot changes address (0 = free). Called from the connection /// classes' set_address(). - // maybe_unused + guard: in a passive proxy (active: false) MAX is 0, the - // body is removed, and the free < MAX compare would trip -Wtype-limits. - void update_address_slot_([[maybe_unused]] uint64_t old_address, [[maybe_unused]] uint64_t new_address) { -#if BLUETOOTH_PROXY_MAX_CONNECTIONS > 0 + void update_address_slot_(uint64_t old_address, uint64_t new_address) { auto &resp = this->connections_free_response_; if (new_address == 0 && old_address != 0) { if (resp.free < BLUETOOTH_PROXY_MAX_CONNECTIONS) { @@ -275,7 +272,6 @@ class BluetoothProxy final : public Component { } this->replace_allocated_slot_(0, new_address); } -#endif // BLUETOOTH_PROXY_MAX_CONNECTIONS > 0 } void replace_allocated_slot_(uint64_t find_value, uint64_t set_value); void log_slot_accounting_mismatch_(); @@ -305,18 +301,20 @@ class BluetoothProxy final : public Component { /// Drops state only, never sends: api_connection_ is the departing /// subscriber on subscribe and nullptr on unsubscribe. void reset_owed_replies_(); +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS /// Report a reply we deliberately do not latch, so no drop is silent. void log_reply_dropped_(const char *what, uint64_t address); /// A latched reply's leading edge; the drain's re-refusals stay quiet. void log_reply_deferred_(const char *what, uint64_t address); /// A latched reply lost to a newer one for a different address. void log_reply_displaced_(const char *what, uint64_t owed, uint64_t address); +#endif // Memory optimized layout for 32-bit systems // Group 1: Pointers (4 bytes each, naturally aligned) api::APIConnection *api_connection_{nullptr}; -#ifdef BLUETOOTH_CONNECTION_HAS_GATT +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // Group 2: Fixed-size array of connection pointers std::array connections_{}; // Address-keyed pool of owed freed-slot notifications; loop() resends. @@ -336,16 +334,20 @@ class BluetoothProxy final : public Component { // BLE advertisement batching api::BluetoothLERawAdvertisementsResponse response_; +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // Pre-allocated response message - always ready to send api::BluetoothConnectionsFreeResponse connections_free_response_; +#endif // Group 4: 1-byte types grouped together bool active_; +#ifdef USE_BLUETOOTH_PROXY_CONNECTIONS // A dropped send (full TCP buffer) would leave the API client with a stale // slot state forever; the cached response is current by construction, so // retrying it from loop() is an idempotent resync. bool connections_free_pending_{false}; uint8_t connection_count_{0}; +#endif bool configured_scan_active_{false}; // Configured scan mode from YAML #ifdef USE_BLE_SCANNER_STATE_CALLBACK // A dropped push (full TX buffer) is re-queried from the hub and resent diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 21cea317497..26b9025d967 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -267,8 +267,10 @@ #ifdef USE_ESP32 #define USE_BLE_SCANNER_STATE_CALLBACK #define BLUETOOTH_PROXY_MAX_CONNECTIONS 3 +#define USE_BLUETOOTH_PROXY_CONNECTIONS #elif defined(USE_RP2) #define BLUETOOTH_PROXY_MAX_CONNECTIONS 3 +#define USE_BLUETOOTH_PROXY_CONNECTIONS #else #define BLUETOOTH_PROXY_MAX_CONNECTIONS 0 #endif diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 451cd9ac1ff..f4eff4a254b 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -2401,7 +2401,10 @@ def get_varint64_ifdef( # At least one 64-bit varint field is unconditional, so the guard must be unconditional. return True, None ifdefs.discard(None) - return True, ifdefs.pop() if len(ifdefs) == 1 else None + # Several guards: the define is needed under any of them, so emit the union. + # Falling back to unconditional would pull 64-bit varint support into builds + # that have none of them. + return True, " || ".join(sorted(ifdefs)) def build_enum_type(desc, enum_ifdef_map) -> tuple[str, str, str]: diff --git a/tests/components/bluetooth_connection/__init__.py b/tests/components/bluetooth_connection/__init__.py index eb6e174c0cd..9c1ad4e74d4 100644 --- a/tests/components/bluetooth_connection/__init__.py +++ b/tests/components/bluetooth_connection/__init__.py @@ -3,7 +3,7 @@ from tests.testing_helpers import ComponentManifestOverride def override_manifest(manifest: ComponentManifestOverride) -> None: - # close_service_batch compiles only under BLUETOOTH_CONNECTION_HAS_GATT; + # close_service_batch compiles only under USE_BLUETOOTH_PROXY_CONNECTIONS; # emit the backend define so the host build exercises it. async def to_code_testing(config): # These defines are global to the merged host test binary; safe @@ -11,6 +11,9 @@ def override_manifest(manifest: ComponentManifestOverride) -> None: cg.add_define("USE_BLE_GATT_CLIENT") cg.add_define("USE_BLE_GATT_CLIENT_STUB_BACKEND") cg.add_define("USE_BLUETOOTH_PROXY") + # Gates the connection half of the API surface, which is what + # close_service_batch and the GATT response types live behind. + cg.add_define("USE_BLUETOOTH_PROXY_CONNECTIONS") cg.add_define("BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE", 16) cg.add_define("BLUETOOTH_PROXY_MAX_CONNECTIONS", 1) diff --git a/tests/unit_tests/components/api/test_api_protobuf_generator.py b/tests/unit_tests/components/api/test_api_protobuf_generator.py new file mode 100644 index 00000000000..2a07cbd49c0 --- /dev/null +++ b/tests/unit_tests/components/api/test_api_protobuf_generator.py @@ -0,0 +1,93 @@ +"""Unit tests for script/api_protobuf/api_protobuf.py generator logic. + +ci-api-proto.yml only checks that the committed output matches what the +generator currently produces, so a semantic regression in the generator would +be committed and matched without anything failing. These tests pin the +semantics directly. +""" + +from __future__ import annotations + +from pathlib import Path +import sys + +import pytest + +sys.path.insert(0, str(Path(__file__).parents[4] / "script" / "api_protobuf")) + +from api_protobuf import _make_ifdef_line, get_varint64_ifdef # noqa: E402 +from google.protobuf import descriptor_pb2 # noqa: E402 + + +def _file_with_messages( + *messages: tuple[str, int, bool], +) -> descriptor_pb2.FileDescriptorProto: + """Build a FileDescriptorProto with one single-field message per entry. + + Each entry is (message_name, field_type, deprecated). + """ + file_desc = descriptor_pb2.FileDescriptorProto(name="test.proto") + for name, field_type, deprecated in messages: + msg = file_desc.message_type.add(name=name) + field = msg.field.add(name="value", number=1, type=field_type) + field.options.deprecated = deprecated + return file_desc + + +UINT64 = descriptor_pb2.FieldDescriptorProto.TYPE_UINT64 +INT64 = descriptor_pb2.FieldDescriptorProto.TYPE_INT64 +SINT64 = descriptor_pb2.FieldDescriptorProto.TYPE_SINT64 +UINT32 = descriptor_pb2.FieldDescriptorProto.TYPE_UINT32 +FIXED64 = descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64 + + +def test_no_varint64_fields() -> None: + file_desc = _file_with_messages(("A", UINT32, False), ("B", FIXED64, False)) + assert get_varint64_ifdef(file_desc, {}) == (False, None) + + +@pytest.mark.parametrize("field_type", [UINT64, INT64, SINT64]) +def test_single_guard_is_kept(field_type: int) -> None: + file_desc = _file_with_messages(("A", field_type, False)) + assert get_varint64_ifdef(file_desc, {"A": "USE_X"}) == (True, "USE_X") + + +def test_two_guards_emit_the_union() -> None: + # The regression this pins: multiple guards used to collapse to + # unconditional, pulling 64-bit varint support into unrelated builds. + file_desc = _file_with_messages(("A", UINT64, False), ("B", INT64, False)) + guards = {"A": "USE_X", "B": "USE_Y"} + assert get_varint64_ifdef(file_desc, guards) == (True, "USE_X || USE_Y") + + +def test_union_is_sorted_for_deterministic_output() -> None: + file_desc = _file_with_messages(("B", UINT64, False), ("A", INT64, False)) + guards = {"B": "USE_Y", "A": "USE_X"} + assert get_varint64_ifdef(file_desc, guards) == (True, "USE_X || USE_Y") + + +def test_any_unconditional_message_wins() -> None: + file_desc = _file_with_messages(("A", UINT64, False), ("B", INT64, False)) + assert get_varint64_ifdef(file_desc, {"A": "USE_X"}) == (True, None) + + +def test_deprecated_fields_and_messages_are_ignored() -> None: + file_desc = _file_with_messages(("A", UINT64, True), ("B", INT64, False)) + file_desc.message_type[1].options.deprecated = True + assert get_varint64_ifdef(file_desc, {"A": "USE_X", "B": "USE_Y"}) == (False, None) + + +def test_make_ifdef_line_simple_identifier() -> None: + assert _make_ifdef_line("USE_X") == "#ifdef USE_X" + + +def test_make_ifdef_line_union_wraps_each_identifier() -> None: + # The second half of the varint64 union guard: compound conditions must + # become #if defined(A) || defined(B), never #ifdef of the raw string. + assert _make_ifdef_line("USE_X || USE_Y") == "#if defined(USE_X) || defined(USE_Y)" + + +def test_make_ifdef_line_conjunction_and_negation() -> None: + assert ( + _make_ifdef_line("USE_X && !USE_Y") == "#if defined(USE_X) && !defined(USE_Y)" + )