mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'integration' into memory_api
This commit is contained in:
		| @@ -117,22 +117,9 @@ CONF_BLE_ID = "ble_id" | ||||
| CONF_IO_CAPABILITY = "io_capability" | ||||
| CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" | ||||
| CONF_DISABLE_BT_LOGS = "disable_bt_logs" | ||||
| CONF_PREFERRED_PHY = "preferred_phy" | ||||
|  | ||||
| NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] | ||||
|  | ||||
| # ESP32 variants that support BLE | ||||
| BLE_VARIANTS = { | ||||
|     const.VARIANT_ESP32, | ||||
|     const.VARIANT_ESP32C3, | ||||
|     const.VARIANT_ESP32S3, | ||||
|     const.VARIANT_ESP32C6, | ||||
|     const.VARIANT_ESP32H2, | ||||
| } | ||||
|  | ||||
| # ESP32 variants that support 2M PHY | ||||
| BLE_2M_PHY_VARIANTS = BLE_VARIANTS - {const.VARIANT_ESP32} | ||||
|  | ||||
| esp32_ble_ns = cg.esphome_ns.namespace("esp32_ble") | ||||
| ESP32BLE = esp32_ble_ns.class_("ESP32BLE", cg.Component) | ||||
|  | ||||
| @@ -153,13 +140,6 @@ IO_CAPABILITY = { | ||||
|     "display_yes_no": IoCapability.IO_CAP_IO, | ||||
| } | ||||
|  | ||||
| BLEPhy = esp32_ble_ns.enum("BLEPhy") | ||||
| BLE_PHY_OPTIONS = { | ||||
|     "1m": BLEPhy.BLE_PHY_1M, | ||||
|     "2m": BLEPhy.BLE_PHY_2M, | ||||
|     "auto": BLEPhy.BLE_PHY_AUTO, | ||||
| } | ||||
|  | ||||
| esp_power_level_t = cg.global_ns.enum("esp_power_level_t") | ||||
|  | ||||
| TX_POWER_LEVELS = { | ||||
| @@ -173,18 +153,6 @@ TX_POWER_LEVELS = { | ||||
|     9: esp_power_level_t.ESP_PWR_LVL_P9, | ||||
| } | ||||
|  | ||||
|  | ||||
| def validate_phy(value: str) -> str: | ||||
|     """Validate PHY selection based on ESP32 variant.""" | ||||
|     variant = get_esp32_variant() | ||||
|     if value == "2m" and variant not in BLE_2M_PHY_VARIANTS: | ||||
|         raise cv.Invalid( | ||||
|             f"2M PHY is not supported on {variant}. " | ||||
|             f"Only supported on: {', '.join(sorted(BLE_2M_PHY_VARIANTS))}" | ||||
|         ) | ||||
|     return value | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(ESP32BLE), | ||||
| @@ -199,10 +167,6 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|         cv.SplitDefault(CONF_DISABLE_BT_LOGS, esp32_idf=True): cv.All( | ||||
|             cv.only_with_esp_idf, cv.boolean | ||||
|         ), | ||||
|         cv.Optional(CONF_PREFERRED_PHY, default="1m"): cv.All( | ||||
|             cv.enum(BLE_PHY_OPTIONS, lower=True), | ||||
|             validate_phy, | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
| @@ -273,7 +237,6 @@ async def to_code(config): | ||||
|     cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) | ||||
|     cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) | ||||
|     cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME])) | ||||
|     cg.add(var.set_preferred_phy(config[CONF_PREFERRED_PHY])) | ||||
|     if (name := config.get(CONF_NAME)) is not None: | ||||
|         cg.add(var.set_name(name)) | ||||
|     await cg.register_component(var, config) | ||||
|   | ||||
| @@ -23,35 +23,6 @@ namespace esphome::esp32_ble { | ||||
|  | ||||
| static const char *const TAG = "esp32_ble"; | ||||
|  | ||||
| static const char *phy_mode_to_string(BLEPhy phy) { | ||||
|   switch (phy) { | ||||
|     case BLE_PHY_1M: | ||||
|       return "1M"; | ||||
|     case BLE_PHY_2M: | ||||
|       return "2M"; | ||||
|     case BLE_PHY_AUTO: | ||||
|       return "AUTO"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
| } | ||||
|  | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ | ||||
|     defined(USE_ESP32_VARIANT_ESP32H2) | ||||
| static uint8_t phy_mode_to_mask(BLEPhy phy) { | ||||
|   switch (phy) { | ||||
|     case BLE_PHY_1M: | ||||
|       return ESP_BLE_GAP_PHY_1M_PREF_MASK; | ||||
|     case BLE_PHY_2M: | ||||
|       return ESP_BLE_GAP_PHY_2M_PREF_MASK; | ||||
|     case BLE_PHY_AUTO: | ||||
|       return ESP_BLE_GAP_PHY_1M_PREF_MASK | ESP_BLE_GAP_PHY_2M_PREF_MASK; | ||||
|     default: | ||||
|       return ESP_BLE_GAP_PHY_1M_PREF_MASK;  // Default to 1M | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void ESP32BLE::setup() { | ||||
|   global_ble = this; | ||||
|   if (!ble_pre_setup_()) { | ||||
| @@ -237,23 +208,6 @@ bool ESP32BLE::ble_setup_() { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   // Configure PHY settings | ||||
| #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ | ||||
|     defined(USE_ESP32_VARIANT_ESP32H2) | ||||
|   // Only newer ESP32 variants support PHY configuration | ||||
|   if (this->preferred_phy_ != BLE_PHY_AUTO) { | ||||
|     uint8_t phy_mask = phy_mode_to_mask(this->preferred_phy_); | ||||
|  | ||||
|     err = esp_ble_gap_set_preferred_default_phy(phy_mask, phy_mask); | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGW(TAG, "esp_ble_gap_set_preferred_default_phy failed: %d", err); | ||||
|       // Not a fatal error, continue | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "Set preferred PHY to %s", phy_mode_to_string(this->preferred_phy_)); | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   // BLE takes some time to be fully set up, 200ms should be more than enough | ||||
|   delay(200);  // NOLINT | ||||
|  | ||||
| @@ -563,10 +517,8 @@ void ESP32BLE::dump_config() { | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "BLE:\n" | ||||
|                   "  MAC address: %s\n" | ||||
|                   "  IO Capability: %s\n" | ||||
|                   "  Preferred PHY: %s", | ||||
|                   format_mac_address_pretty(mac_address).c_str(), io_capability_s, | ||||
|                   phy_mode_to_string(this->preferred_phy_)); | ||||
|                   "  IO Capability: %s", | ||||
|                   format_mac_address_pretty(mac_address).c_str(), io_capability_s); | ||||
|   } else { | ||||
|     ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled"); | ||||
|   } | ||||
|   | ||||
| @@ -55,12 +55,6 @@ enum IoCapability { | ||||
|   IO_CAP_KBDISP = ESP_IO_CAP_KBDISP, | ||||
| }; | ||||
|  | ||||
| enum BLEPhy : uint8_t { | ||||
|   BLE_PHY_1M = 0x01, | ||||
|   BLE_PHY_2M = 0x02, | ||||
|   BLE_PHY_AUTO = 0x03, | ||||
| }; | ||||
|  | ||||
| enum BLEComponentState : uint8_t { | ||||
|   /** Nothing has been initialized yet. */ | ||||
|   BLE_COMPONENT_STATE_OFF = 0, | ||||
| @@ -104,7 +98,6 @@ 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_preferred_phy(BLEPhy phy) { this->preferred_phy_ = phy; } | ||||
|  | ||||
|   void set_advertising_cycle_time(uint32_t advertising_cycle_time) { | ||||
|     this->advertising_cycle_time_ = advertising_cycle_time; | ||||
| @@ -177,7 +170,6 @@ 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 | ||||
|   BLEPhy preferred_phy_{BLE_PHY_1M};                  // 1 byte (uint8_t enum) | ||||
| }; | ||||
|  | ||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <esp_gap_ble_api.h> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_ble_client { | ||||
|  | ||||
| @@ -129,6 +131,25 @@ void BLEClientBase::connect() { | ||||
|   ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(), | ||||
|            this->remote_addr_type_); | ||||
|   this->paired_ = false; | ||||
|  | ||||
|   // For connections without cache, set fast connection parameters before connecting | ||||
|   // This ensures service discovery completes within the 10-second timeout that | ||||
|   // some devices like HomeKit BLE sensors enforce | ||||
|   if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { | ||||
|     auto ret = esp_ble_gap_set_prefer_conn_params(this->remote_bda_, | ||||
|                                                   0x06,   // min_int: 7.5ms | ||||
|                                                   0x06,   // max_int: 7.5ms | ||||
|                                                   0,      // latency: 0 | ||||
|                                                   1000);  // timeout: 10s | ||||
|     if (ret != ESP_OK) { | ||||
|       ESP_LOGW(TAG, "[%d] [%s] esp_ble_gap_set_prefer_conn_params failed: %d", this->connection_index_, | ||||
|                this->address_str_.c_str(), ret); | ||||
|     } else { | ||||
|       ESP_LOGD(TAG, "[%d] [%s] Set preferred connection params for fast discovery (no cache)", this->connection_index_, | ||||
|                this->address_str_.c_str()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   auto ret = esp_ble_gattc_open(this->gattc_if_, this->remote_bda_, this->remote_addr_type_, true); | ||||
|   if (ret) { | ||||
|     ESP_LOGW(TAG, "[%d] [%s] esp_ble_gattc_open error, status=%d", this->connection_index_, this->address_str_.c_str(), | ||||
| @@ -278,12 +299,14 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ | ||||
|                  this->address_str_.c_str(), ret); | ||||
|       } | ||||
|       this->set_state(espbt::ClientState::CONNECTED); | ||||
|       ESP_LOGI(TAG, "[%d] [%s] Connection open", this->connection_index_, this->address_str_.c_str()); | ||||
|       if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) { | ||||
|         ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str()); | ||||
|         ESP_LOGI(TAG, "[%d] [%s] Using cached services", this->connection_index_, this->address_str_.c_str()); | ||||
|         // only set our state, subclients might have more stuff to do yet. | ||||
|         this->state_ = espbt::ClientState::ESTABLISHED; | ||||
|         break; | ||||
|       } | ||||
|       ESP_LOGD(TAG, "[%d] [%s] Searching for services", this->connection_index_, this->address_str_.c_str()); | ||||
|       esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr); | ||||
|       break; | ||||
|     } | ||||
| @@ -296,8 +319,15 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ | ||||
|     case ESP_GATTC_DISCONNECT_EVT: { | ||||
|       if (!this->check_addr(param->disconnect.remote_bda)) | ||||
|         return false; | ||||
|       ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->connection_index_, | ||||
|       // Check if we were disconnected while waiting for service discovery | ||||
|       if (param->disconnect.reason == 0x13 &&  // 0x13 = ESP_GATT_CONN_TERMINATE_PEER | ||||
|           this->state_ == espbt::ClientState::CONNECTED) { | ||||
|         ESP_LOGW(TAG, "[%d] [%s] Disconnected by remote during service discovery", this->connection_index_, | ||||
|                  this->address_str_.c_str()); | ||||
|       } else { | ||||
|         ESP_LOGD(TAG, "[%d] [%s] ESP_GATTC_DISCONNECT_EVT, reason 0x%02x", this->connection_index_, | ||||
|                  this->address_str_.c_str(), param->disconnect.reason); | ||||
|       } | ||||
|       this->release_services(); | ||||
|       this->set_state(espbt::ClientState::IDLE); | ||||
|       break; | ||||
| @@ -353,7 +383,23 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ | ||||
|         ESP_LOGV(TAG, "[%d] [%s]  start_handle: 0x%x  end_handle: 0x%x", this->connection_index_, | ||||
|                  this->address_str_.c_str(), svc->start_handle, svc->end_handle); | ||||
|       } | ||||
|       ESP_LOGI(TAG, "[%d] [%s] Connected", this->connection_index_, this->address_str_.c_str()); | ||||
|       ESP_LOGI(TAG, "[%d] [%s] Service discovery complete", this->connection_index_, this->address_str_.c_str()); | ||||
|  | ||||
|       // For non-cached connections, restore default connection parameters after service discovery | ||||
|       // Now that we've discovered all services, we can use more balanced parameters | ||||
|       // that save power and reduce interference | ||||
|       if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { | ||||
|         esp_ble_conn_update_params_t conn_params = {0}; | ||||
|         memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t)); | ||||
|         conn_params.min_int = 0x0A;  // 12.5ms - ESP-IDF default minimum (BTM_BLE_CONN_INT_MIN_DEF) | ||||
|         conn_params.max_int = 0x0C;  // 15ms - ESP-IDF default maximum (BTM_BLE_CONN_INT_MAX_DEF) | ||||
|         conn_params.latency = 0; | ||||
|         conn_params.timeout = 600;  // 6s - ESP-IDF default timeout (BTM_BLE_CONN_TIMEOUT_DEF) | ||||
|         ESP_LOGD(TAG, "[%d] [%s] Restoring default connection parameters after service discovery", | ||||
|                  this->connection_index_, this->address_str_.c_str()); | ||||
|         esp_ble_gap_update_conn_params(&conn_params); | ||||
|       } | ||||
|  | ||||
|       this->state_ = espbt::ClientState::ESTABLISHED; | ||||
|       break; | ||||
|     } | ||||
|   | ||||
| @@ -57,7 +57,8 @@ from esphome.final_validate import full_config | ||||
|  | ||||
| from . import mipi_dsi_ns, models | ||||
|  | ||||
| DEPENDENCIES = ["esp32"] | ||||
| # Currently only ESP32-P4 is supported, so esp_ldo and psram are required | ||||
| DEPENDENCIES = ["esp32", "esp_ldo", "psram"] | ||||
| DOMAIN = "mipi_dsi" | ||||
|  | ||||
| LOGGER = logging.getLogger(DOMAIN) | ||||
|   | ||||
| @@ -533,9 +533,17 @@ void WiFiComponent::check_scanning_finished() { | ||||
|                        return false; | ||||
|  | ||||
|                      if (a.get_matches() && b.get_matches()) { | ||||
|                        // if both match, check priority | ||||
|                        // For APs with the same SSID, always prefer stronger signal | ||||
|                        // This helps with mesh networks and multiple APs | ||||
|                        if (a.get_ssid() == b.get_ssid()) { | ||||
|                          return a.get_rssi() > b.get_rssi(); | ||||
|                        } | ||||
|  | ||||
|                        // For different SSIDs, check priority first | ||||
|                        if (a.get_priority() != b.get_priority()) | ||||
|                          return a.get_priority() > b.get_priority(); | ||||
|                        // If priorities are equal, prefer stronger signal | ||||
|                        return a.get_rssi() > b.get_rssi(); | ||||
|                      } | ||||
|  | ||||
|                      return a.get_rssi() > b.get_rssi(); | ||||
|   | ||||
| @@ -1,3 +1,2 @@ | ||||
| esp32_ble: | ||||
|   io_capability: keyboard_only | ||||
|   # Default configuration - should use 1m PHY | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| esp32_ble: | ||||
|   preferred_phy: 2m | ||||
| @@ -1,2 +0,0 @@ | ||||
| esp32_ble: | ||||
|   preferred_phy: auto | ||||
| @@ -12,6 +12,8 @@ display: | ||||
|   #- platform: mipi_dsi | ||||
|     #id: backlight_id | ||||
|  | ||||
| psram: | ||||
|  | ||||
| i2c: | ||||
|   sda: GPIO7 | ||||
|   scl: GPIO8 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user