mirror of
				https://github.com/esphome/esphome.git
				synced 2025-11-04 09:01:49 +00:00 
			
		
		
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			2025.10.3
			...
			ble_tx_pow
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					635cb08e63 | 
@@ -3,7 +3,17 @@ import re
 | 
			
		||||
 | 
			
		||||
from esphome import automation
 | 
			
		||||
import esphome.codegen as cg
 | 
			
		||||
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
 | 
			
		||||
from esphome.components.esp32 import (
 | 
			
		||||
    VARIANT_ESP32C2,
 | 
			
		||||
    VARIANT_ESP32C3,
 | 
			
		||||
    VARIANT_ESP32C5,
 | 
			
		||||
    VARIANT_ESP32C6,
 | 
			
		||||
    VARIANT_ESP32H2,
 | 
			
		||||
    VARIANT_ESP32S3,
 | 
			
		||||
    add_idf_sdkconfig_option,
 | 
			
		||||
    const,
 | 
			
		||||
    get_esp32_variant,
 | 
			
		||||
)
 | 
			
		||||
import esphome.config_validation as cv
 | 
			
		||||
from esphome.const import (
 | 
			
		||||
    CONF_ENABLE_ON_BOOT,
 | 
			
		||||
@@ -11,8 +21,10 @@ from esphome.const import (
 | 
			
		||||
    CONF_ID,
 | 
			
		||||
    CONF_NAME,
 | 
			
		||||
    CONF_NAME_ADD_MAC_SUFFIX,
 | 
			
		||||
    CONF_TX_POWER,
 | 
			
		||||
)
 | 
			
		||||
from esphome.core import CORE, TimePeriod
 | 
			
		||||
from esphome.cpp_types import MockObj
 | 
			
		||||
import esphome.final_validate as fv
 | 
			
		||||
 | 
			
		||||
DEPENDENCIES = ["esp32"]
 | 
			
		||||
@@ -151,7 +163,8 @@ IO_CAPABILITY = {
 | 
			
		||||
 | 
			
		||||
esp_power_level_t = cg.global_ns.enum("esp_power_level_t")
 | 
			
		||||
 | 
			
		||||
TX_POWER_LEVELS = {
 | 
			
		||||
# Power level mappings for code generation - ESP32 classic
 | 
			
		||||
TX_POWER_LEVELS_ESP32 = {
 | 
			
		||||
    -12: esp_power_level_t.ESP_PWR_LVL_N12,
 | 
			
		||||
    -9: esp_power_level_t.ESP_PWR_LVL_N9,
 | 
			
		||||
    -6: esp_power_level_t.ESP_PWR_LVL_N6,
 | 
			
		||||
@@ -162,6 +175,53 @@ TX_POWER_LEVELS = {
 | 
			
		||||
    9: esp_power_level_t.ESP_PWR_LVL_P9,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Power level mappings for code generation - Extended variants
 | 
			
		||||
TX_POWER_LEVELS_EXT = {
 | 
			
		||||
    -24: esp_power_level_t.ESP_PWR_LVL_N24,
 | 
			
		||||
    -21: esp_power_level_t.ESP_PWR_LVL_N21,
 | 
			
		||||
    -18: esp_power_level_t.ESP_PWR_LVL_N18,
 | 
			
		||||
    -15: esp_power_level_t.ESP_PWR_LVL_N15,
 | 
			
		||||
    -12: esp_power_level_t.ESP_PWR_LVL_N12,
 | 
			
		||||
    -9: esp_power_level_t.ESP_PWR_LVL_N9,
 | 
			
		||||
    -6: esp_power_level_t.ESP_PWR_LVL_N6,
 | 
			
		||||
    -3: esp_power_level_t.ESP_PWR_LVL_N3,
 | 
			
		||||
    0: esp_power_level_t.ESP_PWR_LVL_N0,
 | 
			
		||||
    3: esp_power_level_t.ESP_PWR_LVL_P3,
 | 
			
		||||
    6: esp_power_level_t.ESP_PWR_LVL_P6,
 | 
			
		||||
    9: esp_power_level_t.ESP_PWR_LVL_P9,
 | 
			
		||||
    12: esp_power_level_t.ESP_PWR_LVL_P12,
 | 
			
		||||
    15: esp_power_level_t.ESP_PWR_LVL_P15,
 | 
			
		||||
    18: esp_power_level_t.ESP_PWR_LVL_P18,
 | 
			
		||||
    20: esp_power_level_t.ESP_PWR_LVL_P20,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_tx_power_levels() -> dict[str, MockObj]:
 | 
			
		||||
    variant = get_esp32_variant()
 | 
			
		||||
    if variant in [
 | 
			
		||||
        VARIANT_ESP32C2,
 | 
			
		||||
        VARIANT_ESP32C3,
 | 
			
		||||
        VARIANT_ESP32C5,
 | 
			
		||||
        VARIANT_ESP32C6,
 | 
			
		||||
        VARIANT_ESP32H2,
 | 
			
		||||
        VARIANT_ESP32S3,
 | 
			
		||||
    ]:
 | 
			
		||||
        return TX_POWER_LEVELS_EXT
 | 
			
		||||
    return TX_POWER_LEVELS_ESP32
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def validate_tx_power(value: int) -> int:
 | 
			
		||||
    value = cv.decibel(value)
 | 
			
		||||
    power_levels = _get_tx_power_levels()
 | 
			
		||||
    if value not in power_levels:
 | 
			
		||||
        raise cv.Invalid(
 | 
			
		||||
            f"TX power {value}dBm is not valid. "
 | 
			
		||||
            f"Valid values are: {', '.join(str(v) + 'dBm' for v in sorted(power_levels.keys()))}"
 | 
			
		||||
        )
 | 
			
		||||
    # Return just the dBm value, we'll map it to enum in to_code
 | 
			
		||||
    return value
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
    {
 | 
			
		||||
        cv.GenerateID(): cv.declare_id(ESP32BLE),
 | 
			
		||||
@@ -169,6 +229,7 @@ CONFIG_SCHEMA = cv.Schema(
 | 
			
		||||
        cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
 | 
			
		||||
            IO_CAPABILITY, lower=True
 | 
			
		||||
        ),
 | 
			
		||||
        cv.Optional(CONF_TX_POWER): validate_tx_power,
 | 
			
		||||
        cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
 | 
			
		||||
        cv.Optional(CONF_ADVERTISING, default=False): cv.boolean,
 | 
			
		||||
        cv.Optional(
 | 
			
		||||
@@ -259,6 +320,9 @@ async def to_code(config):
 | 
			
		||||
    cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME]))
 | 
			
		||||
    if (name := config.get(CONF_NAME)) is not None:
 | 
			
		||||
        cg.add(var.set_name(name))
 | 
			
		||||
    if (tx_power := config.get(CONF_TX_POWER)) is not None:
 | 
			
		||||
        # The validation already returned the enum value
 | 
			
		||||
        cg.add(var.set_tx_power(_get_tx_power_levels()[tx_power]))
 | 
			
		||||
    await cg.register_component(var, config)
 | 
			
		||||
 | 
			
		||||
    if CORE.using_esp_idf:
 | 
			
		||||
 
 | 
			
		||||
@@ -212,6 +212,15 @@ bool ESP32BLE::ble_setup_() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Set TX power for all BLE operations (advertising, scanning, connections)
 | 
			
		||||
  err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, this->tx_power_);
 | 
			
		||||
  if (err != ESP_OK) {
 | 
			
		||||
    ESP_LOGW(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err));
 | 
			
		||||
    // Continue anyway as this is not critical
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGD(TAG, "BLE TX power set to level %d", this->tx_power_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // BLE takes some time to be fully set up, 200ms should be more than enough
 | 
			
		||||
  delay(200);  // NOLINT
 | 
			
		||||
 | 
			
		||||
@@ -520,11 +529,106 @@ void ESP32BLE::dump_config() {
 | 
			
		||||
        io_capability_s = "invalid";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    // Convert TX power level to dBm for display
 | 
			
		||||
    int tx_power_dbm = 0;
 | 
			
		||||
#if defined(CONFIG_IDF_TARGET_ESP32)
 | 
			
		||||
    // ESP32 classic power levels (0-7)
 | 
			
		||||
    switch (this->tx_power_) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        tx_power_dbm = -12;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N12
 | 
			
		||||
      case 1:
 | 
			
		||||
        tx_power_dbm = -9;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N9
 | 
			
		||||
      case 2:
 | 
			
		||||
        tx_power_dbm = -6;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N6
 | 
			
		||||
      case 3:
 | 
			
		||||
        tx_power_dbm = -3;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N3
 | 
			
		||||
      case 4:
 | 
			
		||||
        tx_power_dbm = 0;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N0
 | 
			
		||||
      case 5:
 | 
			
		||||
        tx_power_dbm = 3;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P3
 | 
			
		||||
      case 6:
 | 
			
		||||
        tx_power_dbm = 6;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P6
 | 
			
		||||
      case 7:
 | 
			
		||||
        tx_power_dbm = 9;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P9
 | 
			
		||||
      default:
 | 
			
		||||
        tx_power_dbm = 0;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || \
 | 
			
		||||
    defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || \
 | 
			
		||||
    defined(CONFIG_IDF_TARGET_ESP32S3)
 | 
			
		||||
    // Extended power levels for C2/C3/C5/C6/H2/S3 (0-15)
 | 
			
		||||
    switch (this->tx_power_) {
 | 
			
		||||
      case 0:
 | 
			
		||||
        tx_power_dbm = -24;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N24
 | 
			
		||||
      case 1:
 | 
			
		||||
        tx_power_dbm = -21;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N21
 | 
			
		||||
      case 2:
 | 
			
		||||
        tx_power_dbm = -18;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N18
 | 
			
		||||
      case 3:
 | 
			
		||||
        tx_power_dbm = -15;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N15
 | 
			
		||||
      case 4:
 | 
			
		||||
        tx_power_dbm = -12;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N12
 | 
			
		||||
      case 5:
 | 
			
		||||
        tx_power_dbm = -9;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N9
 | 
			
		||||
      case 6:
 | 
			
		||||
        tx_power_dbm = -6;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N6
 | 
			
		||||
      case 7:
 | 
			
		||||
        tx_power_dbm = -3;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N3
 | 
			
		||||
      case 8:
 | 
			
		||||
        tx_power_dbm = 0;
 | 
			
		||||
        break;  // ESP_PWR_LVL_N0
 | 
			
		||||
      case 9:
 | 
			
		||||
        tx_power_dbm = 3;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P3
 | 
			
		||||
      case 10:
 | 
			
		||||
        tx_power_dbm = 6;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P6
 | 
			
		||||
      case 11:
 | 
			
		||||
        tx_power_dbm = 9;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P9
 | 
			
		||||
      case 12:
 | 
			
		||||
        tx_power_dbm = 12;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P12
 | 
			
		||||
      case 13:
 | 
			
		||||
        tx_power_dbm = 15;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P15
 | 
			
		||||
      case 14:
 | 
			
		||||
        tx_power_dbm = 18;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P18
 | 
			
		||||
      case 15:
 | 
			
		||||
        tx_power_dbm = 20;
 | 
			
		||||
        break;  // ESP_PWR_LVL_P20
 | 
			
		||||
      default:
 | 
			
		||||
        tx_power_dbm = 0;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    // Unknown variant
 | 
			
		||||
    tx_power_dbm = 0;
 | 
			
		||||
#endif
 | 
			
		||||
    ESP_LOGCONFIG(TAG,
 | 
			
		||||
                  "BLE:\n"
 | 
			
		||||
                  "  MAC address: %s\n"
 | 
			
		||||
                  "  IO Capability: %s",
 | 
			
		||||
                  format_mac_address_pretty(mac_address).c_str(), io_capability_s);
 | 
			
		||||
                  "  IO Capability: %s\n"
 | 
			
		||||
                  "  TX Power: %d dBm",
 | 
			
		||||
                  format_mac_address_pretty(mac_address).c_str(), io_capability_s, tx_power_dbm);
 | 
			
		||||
  } else {
 | 
			
		||||
    ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 | 
			
		||||
#ifdef USE_ESP32
 | 
			
		||||
 | 
			
		||||
#include <esp_bt.h>
 | 
			
		||||
#include <esp_gap_ble_api.h>
 | 
			
		||||
#include <esp_gattc_api.h>
 | 
			
		||||
#include <esp_gatts_api.h>
 | 
			
		||||
@@ -94,6 +95,7 @@ class BLEStatusEventHandler {
 | 
			
		||||
class ESP32BLE : public Component {
 | 
			
		||||
 public:
 | 
			
		||||
  void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
 | 
			
		||||
  void set_tx_power(esp_power_level_t tx_power) { this->tx_power_ = tx_power; }
 | 
			
		||||
 | 
			
		||||
  void set_advertising_cycle_time(uint32_t advertising_cycle_time) {
 | 
			
		||||
    this->advertising_cycle_time_ = advertising_cycle_time;
 | 
			
		||||
@@ -172,6 +174,7 @@ class ESP32BLE : public Component {
 | 
			
		||||
  // 1-byte aligned members (grouped together to minimize padding)
 | 
			
		||||
  BLEComponentState state_{BLE_COMPONENT_STATE_OFF};  // 1 byte (uint8_t enum)
 | 
			
		||||
  bool enable_on_boot_{};                             // 1 byte
 | 
			
		||||
  esp_power_level_t tx_power_{ESP_PWR_LVL_P9};        // 1 byte (default: +9 dBm)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user