From 63882c4a74c61978edf8de2cb813b2118480c131 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 10 Jun 2025 18:57:43 -0500 Subject: [PATCH] Reduce Bluetooth overhead by disabling unused logging categories (#8945) --- esphome/components/ble_client/__init__.py | 6 +- .../components/bluetooth_proxy/__init__.py | 6 +- esphome/components/esp32_ble/__init__.py | 115 ++++++++++++++++++ .../components/esp32_ble_server/__init__.py | 5 +- .../components/esp32_ble_tracker/__init__.py | 4 + esphome/components/esp32_improv/__init__.py | 6 +- .../esp32_ble/test.esp32-c3-idf.yaml | 4 + .../components/esp32_ble/test.esp32-idf.yaml | 4 + 8 files changed, 146 insertions(+), 4 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 37f8ea32b3..a88172ca87 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,7 +1,8 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import ( CONF_CHARACTERISTIC_UUID, @@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await esp32_ble_tracker.register_client(var, config) diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index 04ac9116c7..5c144cadcc 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg -from esphome.components import esp32_ble_client, esp32_ble_tracker +from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ACTIVE, CONF_ID @@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.L2CAP, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 37b4900a03..93bb643596 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,3 +1,4 @@ +from enum import Enum import re from esphome import automation @@ -12,9 +13,110 @@ import esphome.final_validate as fv DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] + +class BTLoggers(Enum): + """Bluetooth logger categories available in ESP-IDF. + + Each logger controls debug output for a specific Bluetooth subsystem. + The value is the ESP-IDF sdkconfig option name for controlling the log level. + """ + + # Core Stack Layers + HCI = "CONFIG_BT_LOG_HCI_TRACE_LEVEL" + """Host Controller Interface - Low-level interface between host and controller""" + + BTM = "CONFIG_BT_LOG_BTM_TRACE_LEVEL" + """Bluetooth Manager - Core device control, connections, and security""" + + L2CAP = "CONFIG_BT_LOG_L2CAP_TRACE_LEVEL" + """Logical Link Control and Adaptation Protocol - Connection multiplexing""" + + RFCOMM = "CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL" + """Serial port emulation over Bluetooth (Classic only)""" + + SDP = "CONFIG_BT_LOG_SDP_TRACE_LEVEL" + """Service Discovery Protocol - Service discovery (Classic only)""" + + GAP = "CONFIG_BT_LOG_GAP_TRACE_LEVEL" + """Generic Access Profile - Device discovery and connections""" + + # Network Protocols + BNEP = "CONFIG_BT_LOG_BNEP_TRACE_LEVEL" + """Bluetooth Network Encapsulation Protocol - IP over Bluetooth""" + + PAN = "CONFIG_BT_LOG_PAN_TRACE_LEVEL" + """Personal Area Networking - Ethernet over Bluetooth""" + + # Audio/Video Profiles (Classic Bluetooth) + A2D = "CONFIG_BT_LOG_A2D_TRACE_LEVEL" + """Advanced Audio Distribution - A2DP audio streaming""" + + AVDT = "CONFIG_BT_LOG_AVDT_TRACE_LEVEL" + """Audio/Video Distribution Transport - A2DP transport protocol""" + + AVCT = "CONFIG_BT_LOG_AVCT_TRACE_LEVEL" + """Audio/Video Control Transport - AVRCP transport protocol""" + + AVRC = "CONFIG_BT_LOG_AVRC_TRACE_LEVEL" + """Audio/Video Remote Control - Media playback control""" + + # Security + SMP = "CONFIG_BT_LOG_SMP_TRACE_LEVEL" + """Security Manager Protocol - BLE pairing and encryption""" + + # Application Layer + BTIF = "CONFIG_BT_LOG_BTIF_TRACE_LEVEL" + """Bluetooth Interface - Application interface layer""" + + BTC = "CONFIG_BT_LOG_BTC_TRACE_LEVEL" + """Bluetooth Common - Task handling and coordination""" + + # BLE Specific + BLE_SCAN = "CONFIG_BT_LOG_BLE_SCAN_TRACE_LEVEL" + """BLE scanning operations""" + + GATT = "CONFIG_BT_LOG_GATT_TRACE_LEVEL" + """Generic Attribute Profile - BLE data exchange protocol""" + + # Other Profiles + MCA = "CONFIG_BT_LOG_MCA_TRACE_LEVEL" + """Multi-Channel Adaptation - Health device profile""" + + HID = "CONFIG_BT_LOG_HID_TRACE_LEVEL" + """Human Interface Device - Keyboards, mice, controllers""" + + APPL = "CONFIG_BT_LOG_APPL_TRACE_LEVEL" + """Application layer logging""" + + OSI = "CONFIG_BT_LOG_OSI_TRACE_LEVEL" + """OS abstraction layer - Threading, memory, timers""" + + BLUFI = "CONFIG_BT_LOG_BLUFI_TRACE_LEVEL" + """ESP32 WiFi provisioning over Bluetooth""" + + +# Set to track which loggers are needed by components +_required_loggers: set[BTLoggers] = set() + + +def register_bt_logger(*loggers: BTLoggers) -> None: + """Register Bluetooth logger categories that a component needs. + + Args: + *loggers: One or more BTLoggers enum members + """ + for logger in loggers: + if not isinstance(logger, BTLoggers): + raise TypeError( + f"Logger must be a BTLoggers enum member, got {type(logger)}" + ) + _required_loggers.add(logger) + + CONF_BLE_ID = "ble_id" CONF_IO_CAPABILITY = "io_capability" CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" +CONF_DISABLE_BT_LOGS = "disable_bt_logs" NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] @@ -62,6 +164,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional( CONF_ADVERTISING_CYCLE_TIME, default="10s" ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_DISABLE_BT_LOGS, esp32_idf=True): cv.All( + cv.only_with_esp_idf, cv.boolean + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -140,6 +245,16 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True) + # Register the core BLE loggers that are always needed + register_bt_logger(BTLoggers.GAP, BTLoggers.BTM, BTLoggers.HCI) + + # Apply logger settings if log disabling is enabled + if config.get(CONF_DISABLE_BT_LOGS, False): + # Disable all Bluetooth loggers that are not required + for logger in BTLoggers: + if logger not in _required_loggers: + add_idf_sdkconfig_option(f"{logger.value}_NONE", True) + cg.add_define("USE_ESP32_BLE") diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 0fcb5c9822..773445a1a7 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -4,7 +4,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option -from esphome.components.esp32_ble import bt_uuid +from esphome.components.esp32_ble import BTLoggers, bt_uuid import esphome.config_validation as cv from esphome.config_validation import UNDEFINED from esphome.const import ( @@ -525,6 +525,9 @@ async def to_code_characteristic(service_var, char_conf): async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index b7eddeb0dd..61eed1c029 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -9,6 +9,7 @@ import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32_ble import ( + BTLoggers, bt_uuid, bt_uuid16_format, bt_uuid32_format, @@ -259,6 +260,9 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.BLE_SCAN) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index ca39c1cd36..fa33bd947a 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import binary_sensor, output +from esphome.components import binary_sensor, esp32_ble, output +from esphome.components.esp32_ble import BTLoggers import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID @@ -94,6 +95,9 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): + # Register the loggers this component needs + esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP) + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) diff --git a/tests/components/esp32_ble/test.esp32-c3-idf.yaml b/tests/components/esp32_ble/test.esp32-c3-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false diff --git a/tests/components/esp32_ble/test.esp32-idf.yaml b/tests/components/esp32_ble/test.esp32-idf.yaml index dade44d145..f8defaf28f 100644 --- a/tests/components/esp32_ble/test.esp32-idf.yaml +++ b/tests/components/esp32_ble/test.esp32-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +esp32_ble: + io_capability: keyboard_only + disable_bt_logs: false