From 460eb219ba139a5eff179b8896999b7fde42931c Mon Sep 17 00:00:00 2001 From: Patrick Van Oosterwijck Date: Thu, 25 Sep 2025 15:45:07 -0600 Subject: [PATCH 1/9] [ethernet] Add LAN8670 PHY support (#10874) Co-authored-by: J. Nick Koston --- esphome/components/ethernet/__init__.py | 23 ++++++++++++++++++- .../ethernet/ethernet_component.cpp | 16 +++++++++++++ .../components/ethernet/ethernet_component.h | 1 + esphome/idf_component.yml | 4 ++++ tests/components/ethernet/common-lan8670.yaml | 14 +++++++++++ .../ethernet/test-lan8670.esp32-ard.yaml | 1 + .../ethernet/test-lan8670.esp32-idf.yaml | 1 + 7 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/components/ethernet/common-lan8670.yaml create mode 100644 tests/components/ethernet/test-lan8670.esp32-ard.yaml create mode 100644 tests/components/ethernet/test-lan8670.esp32-idf.yaml diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index ef8e82a843..75b09dc8e4 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -2,9 +2,15 @@ import logging from esphome import pins import esphome.codegen as cg -from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant +from esphome.components.esp32 import ( + add_idf_component, + add_idf_sdkconfig_option, + get_esp32_variant, +) from esphome.components.esp32.const import ( + VARIANT_ESP32, VARIANT_ESP32C3, + VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -75,12 +81,14 @@ ETHERNET_TYPES = { "W5500": EthernetType.ETHERNET_TYPE_W5500, "OPENETH": EthernetType.ETHERNET_TYPE_OPENETH, "DM9051": EthernetType.ETHERNET_TYPE_DM9051, + "LAN8670": EthernetType.ETHERNET_TYPE_LAN8670, } # PHY types that need compile-time defines for conditional compilation _PHY_TYPE_TO_DEFINE = { "KSZ8081": "USE_ETHERNET_KSZ8081", "KSZ8081RNA": "USE_ETHERNET_KSZ8081", + "LAN8670": "USE_ETHERNET_LAN8670", # Add other PHY types here only if they need conditional compilation } @@ -136,6 +144,14 @@ def _validate(config): else: use_address = CORE.name + config[CONF_DOMAIN] config[CONF_USE_ADDRESS] = use_address + + # Validate LAN8670 is only used with ESP32 classic or ESP32-P4 + if config[CONF_TYPE] == "LAN8670": + variant = get_esp32_variant() + if variant not in (VARIANT_ESP32, VARIANT_ESP32P4): + raise cv.Invalid( + f"LAN8670 PHY is only supported on ESP32 classic and ESP32-P4, not {variant}" + ) if config[CONF_TYPE] in SPI_ETHERNET_TYPES: if _is_framework_spi_polling_mode_supported(): if CONF_POLLING_INTERVAL in config and CONF_INTERRUPT_PIN in config: @@ -248,6 +264,7 @@ CONFIG_SCHEMA = cv.All( "W5500": SPI_SCHEMA, "OPENETH": BASE_SCHEMA, "DM9051": SPI_SCHEMA, + "LAN8670": RMII_SCHEMA, }, upper=True, ), @@ -356,5 +373,9 @@ async def to_code(config): # Also disable WiFi/BT coexistence since WiFi is disabled add_idf_sdkconfig_option("CONFIG_SW_COEXIST_ENABLE", False) + if config[CONF_TYPE] == "LAN8670": + # Add LAN867x 10BASE-T1S PHY support component + add_idf_component(name="espressif/lan867x", ref="2.0.0") + if CORE.using_arduino: cg.add_library("WiFi", None) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index ff14d19427..cb43b2c83c 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -9,6 +9,10 @@ #include #include "esp_event.h" +#ifdef USE_ETHERNET_LAN8670 +#include "esp_eth_phy_lan867x.h" +#endif + #ifdef USE_ETHERNET_SPI #include #include @@ -200,6 +204,12 @@ void EthernetComponent::setup() { this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config); break; } +#ifdef USE_ETHERNET_LAN8670 + case ETHERNET_TYPE_LAN8670: { + this->phy_ = esp_eth_phy_new_lan867x(&phy_config); + break; + } +#endif #endif #ifdef USE_ETHERNET_SPI #if CONFIG_ETH_SPI_ETHERNET_W5500 @@ -353,6 +363,12 @@ void EthernetComponent::dump_config() { eth_type = "DM9051"; break; +#ifdef USE_ETHERNET_LAN8670 + case ETHERNET_TYPE_LAN8670: + eth_type = "LAN8670"; + break; +#endif + default: eth_type = "Unknown"; break; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index bbb9d7fb60..fae5bb1257 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -28,6 +28,7 @@ enum EthernetType : uint8_t { ETHERNET_TYPE_W5500, ETHERNET_TYPE_OPENETH, ETHERNET_TYPE_DM9051, + ETHERNET_TYPE_LAN8670, }; struct ManualIP { diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 687efd2b49..1a6dc8b97d 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -19,3 +19,7 @@ dependencies: - if: "target in [esp32h2, esp32p4]" zorxx/multipart-parser: version: 1.0.1 + espressif/lan867x: + version: "2.0.0" + rules: + - if: "target in [esp32, esp32p4]" diff --git a/tests/components/ethernet/common-lan8670.yaml b/tests/components/ethernet/common-lan8670.yaml new file mode 100644 index 0000000000..ec2f24273d --- /dev/null +++ b/tests/components/ethernet/common-lan8670.yaml @@ -0,0 +1,14 @@ +ethernet: + type: LAN8670 + mdc_pin: 23 + mdio_pin: 25 + clk: + pin: 0 + mode: CLK_EXT_IN + phy_addr: 0 + power_pin: 26 + manual_ip: + static_ip: 192.168.178.56 + gateway: 192.168.178.1 + subnet: 255.255.255.0 + domain: .local diff --git a/tests/components/ethernet/test-lan8670.esp32-ard.yaml b/tests/components/ethernet/test-lan8670.esp32-ard.yaml new file mode 100644 index 0000000000..914a06ae88 --- /dev/null +++ b/tests/components/ethernet/test-lan8670.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common-lan8670.yaml diff --git a/tests/components/ethernet/test-lan8670.esp32-idf.yaml b/tests/components/ethernet/test-lan8670.esp32-idf.yaml new file mode 100644 index 0000000000..914a06ae88 --- /dev/null +++ b/tests/components/ethernet/test-lan8670.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common-lan8670.yaml From eeb3ccaef76ac2df852990a7ac29bcb6f5b63d46 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 26 Sep 2025 11:13:23 +1200 Subject: [PATCH 2/9] Update stale bot rules (#10891) --- .github/workflows/stale.yml | 48 +++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index da1e62c8e1..eebe583fe5 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,14 +15,21 @@ concurrency: jobs: stale: + if: github.repository_owner == 'esphome' runs-on: ubuntu-latest steps: - - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 + # The 90 day stale policy for PRs + # - PRs + # - No PRs marked as "not-stale" + # - No Issues (see below) + - name: 90 days stale PRs + uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: - days-before-pr-stale: 90 - days-before-pr-close: 7 + days-before-stale: 90 + days-before-close: 7 days-before-issue-stale: -1 days-before-issue-close: -1 + operations-per-run: 150 remove-stale-when-updated: true stale-pr-label: "stale" exempt-pr-labels: "not-stale" @@ -30,21 +37,36 @@ jobs: There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days. - Thank you for your contributions. - # Use stale to automatically close issues with a - # reference to the issue tracker - close-issues: - runs-on: ubuntu-latest - steps: - - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 + If you are the author of this PR, please leave a comment if you want + to keep it open. Also, please rebase your PR onto the latest dev + branch to ensure that it's up to date with the latest changes. + + Thank you for your contribution! + + # The 90 day stale policy for Issues + # - Issues + # - No Issues marked as "not-stale" + # - No PRs (see above) + - name: 90 days stale issues + uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: + days-before-stale: 90 + days-before-close: 7 days-before-pr-stale: -1 days-before-pr-close: -1 - days-before-issue-stale: 1 - days-before-issue-close: 1 remove-stale-when-updated: true stale-issue-label: "stale" exempt-issue-labels: "not-stale" stale-issue-message: > - https://github.com/esphome/esphome/issues/430 + There hasn't been any activity on this issue recently. Due to the + high number of incoming GitHub notifications, we have to clean some + of the old issues, as many of them have already been resolved with + the latest updates. + + Please make sure to update to the latest ESPHome version and + check if that solves the issue. Let us know if that works for you by + adding a comment 👍 + + This issue has now been marked as stale and will be closed if no + further activity occurs. Thank you for your contributions. From 28199c1cf8a241a29535251339f1b71eaf8647d2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 26 Sep 2025 12:02:51 +1200 Subject: [PATCH 3/9] [stale] Clean up stale job (#10892) --- .github/workflows/stale.yml | 38 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index eebe583fe5..f57f0987ec 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,19 +18,19 @@ jobs: if: github.repository_owner == 'esphome' runs-on: ubuntu-latest steps: - # The 90 day stale policy for PRs - # - PRs - # - No PRs marked as "not-stale" - # - No Issues (see below) - - name: 90 days stale PRs + - name: Stale uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: - days-before-stale: 90 - days-before-close: 7 - days-before-issue-stale: -1 - days-before-issue-close: -1 - operations-per-run: 150 + debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch remove-stale-when-updated: true + operations-per-run: 150 + + # The 90 day stale policy for PRs + # - PRs + # - No PRs marked as "not-stale" + # - No Issues (see below) + days-before-pr-stale: 90 + days-before-pr-close: 7 stale-pr-label: "stale" exempt-pr-labels: "not-stale" stale-pr-message: > @@ -44,18 +44,12 @@ jobs: Thank you for your contribution! - # The 90 day stale policy for Issues - # - Issues - # - No Issues marked as "not-stale" - # - No PRs (see above) - - name: 90 days stale issues - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 - with: - days-before-stale: 90 - days-before-close: 7 - days-before-pr-stale: -1 - days-before-pr-close: -1 - remove-stale-when-updated: true + # The 90 day stale policy for Issues + # - Issues + # - No Issues marked as "not-stale" + # - No PRs (see above) + days-before-issue-stale: 90 + days-before-issue-close: 7 stale-issue-label: "stale" exempt-issue-labels: "not-stale" stale-issue-message: > From 250b94d113d4be151bd6ebdfa75e94284825db73 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:10:31 -0500 Subject: [PATCH 4/9] [text_sensor] Convert LOG_TEXT_SENSOR macro to function to reduce flash usage (#10884) --- esphome/components/ccs811/ccs811.cpp | 2 +- esphome/components/text_sensor/text_sensor.cpp | 16 ++++++++++++++++ esphome/components/text_sensor/text_sensor.h | 13 +++---------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 40c5318339..84355f2793 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -155,7 +155,7 @@ void CCS811Component::dump_config() { LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "CO2 Sensor", this->co2_); LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_); - LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_) + LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_); if (this->baseline_) { ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_); } else { diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index 72b540b84c..17bf20466e 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -6,6 +6,22 @@ namespace text_sensor { static const char *const TAG = "text_sensor"; +void log_text_sensor(const char *tag, const char *prefix, const char *type, TextSensor *obj) { + if (obj == nullptr) { + return; + } + + ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str()); + + if (!obj->get_device_class_ref().empty()) { + ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class_ref().c_str()); + } + + if (!obj->get_icon_ref().empty()) { + ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon_ref().c_str()); + } +} + void TextSensor::publish_state(const std::string &state) { this->raw_state = state; if (this->raw_callback_) { diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 3ab88e2d91..abbea27b59 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -11,16 +11,9 @@ namespace esphome { namespace text_sensor { -#define LOG_TEXT_SENSOR(prefix, type, obj) \ - if ((obj) != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \ - if (!(obj)->get_device_class_ref().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class_ref().c_str()); \ - } \ - if (!(obj)->get_icon_ref().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon_ref().c_str()); \ - } \ - } +void log_text_sensor(const char *tag, const char *prefix, const char *type, TextSensor *obj); + +#define LOG_TEXT_SENSOR(prefix, type, obj) log_text_sensor(TAG, prefix, LOG_STR_LITERAL(type), obj) #define SUB_TEXT_SENSOR(name) \ protected: \ From 2b12ff58746bfd6836890205b60267e1eea9d683 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:11:13 -0500 Subject: [PATCH 5/9] [esp32_ble] Reduce RAM usage and firmware size by disabling unused GATT functionality (#10862) --- esphome/components/esp32_ble/__init__.py | 13 +++++++++++++ esphome/components/esp32_ble/ble.cpp | 20 ++++++++++++++++++++ esphome/components/esp32_ble/ble.h | 16 ++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index dae9799028..0501d1c5ef 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -242,6 +242,19 @@ def final_validation(config): f"Name '{name}' is too long, maximum length is {max_length} characters" ) + # Set GATT Client/Server sdkconfig options based on which components are loaded + full_config = fv.full_config.get() + + # Check if BLE Server is needed + has_ble_server = "esp32_ble_server" in full_config + add_idf_sdkconfig_option("CONFIG_BT_GATTS_ENABLE", has_ble_server) + + # Check if BLE Client is needed (via esp32_ble_tracker or esp32_ble_client) + has_ble_client = ( + "esp32_ble_tracker" in full_config or "esp32_ble_client" in full_config + ) + add_idf_sdkconfig_option("CONFIG_BT_GATTC_ENABLE", has_ble_client) + return config diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index e22d43c0cc..6b6b19e079 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -167,6 +167,7 @@ bool ESP32BLE::ble_setup_() { } } +#ifdef USE_ESP32_BLE_SERVER if (!this->gatts_event_handlers_.empty()) { err = esp_ble_gatts_register_callback(ESP32BLE::gatts_event_handler); if (err != ESP_OK) { @@ -174,7 +175,9 @@ bool ESP32BLE::ble_setup_() { return false; } } +#endif +#ifdef USE_ESP32_BLE_CLIENT if (!this->gattc_event_handlers_.empty()) { err = esp_ble_gattc_register_callback(ESP32BLE::gattc_event_handler); if (err != ESP_OK) { @@ -182,6 +185,7 @@ bool ESP32BLE::ble_setup_() { return false; } } +#endif std::string name; if (this->name_.has_value()) { @@ -303,6 +307,7 @@ void ESP32BLE::loop() { BLEEvent *ble_event = this->ble_events_.pop(); while (ble_event != nullptr) { switch (ble_event->type_) { +#ifdef USE_ESP32_BLE_SERVER case BLEEvent::GATTS: { esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event; esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if; @@ -313,6 +318,8 @@ void ESP32BLE::loop() { } break; } +#endif +#ifdef USE_ESP32_BLE_CLIENT case BLEEvent::GATTC: { esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event; esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if; @@ -323,6 +330,7 @@ void ESP32BLE::loop() { } break; } +#endif case BLEEvent::GAP: { esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event; switch (gap_event) { @@ -416,13 +424,17 @@ void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_pa event->load_gap_event(e, p); } +#ifdef USE_ESP32_BLE_CLIENT void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) { event->load_gattc_event(e, i, p); } +#endif +#ifdef USE_ESP32_BLE_SERVER void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) { event->load_gatts_event(e, i, p); } +#endif template void enqueue_ble_event(Args... args) { // Allocate an event from the pool @@ -443,8 +455,12 @@ template void enqueue_ble_event(Args... args) { // Explicit template instantiations for the friend function template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *); +#ifdef USE_ESP32_BLE_SERVER template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *); +#endif +#ifdef USE_ESP32_BLE_CLIENT template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *); +#endif void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { @@ -484,15 +500,19 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event); } +#ifdef USE_ESP32_BLE_SERVER void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { enqueue_ble_event(event, gatts_if, param); } +#endif +#ifdef USE_ESP32_BLE_CLIENT void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { enqueue_ble_event(event, gattc_if, param); } +#endif float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 712787fe53..368ac644cf 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -74,17 +74,21 @@ class GAPScanEventHandler { virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0; }; +#ifdef USE_ESP32_BLE_CLIENT class GATTcEventHandler { public: virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) = 0; }; +#endif +#ifdef USE_ESP32_BLE_SERVER class GATTsEventHandler { public: virtual void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) = 0; }; +#endif class BLEStatusEventHandler { public: @@ -123,16 +127,24 @@ class ESP32BLE : public Component { void register_gap_scan_event_handler(GAPScanEventHandler *handler) { this->gap_scan_event_handlers_.push_back(handler); } +#ifdef USE_ESP32_BLE_CLIENT void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } +#endif +#ifdef USE_ESP32_BLE_SERVER void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); } +#endif void register_ble_status_event_handler(BLEStatusEventHandler *handler) { this->ble_status_event_handlers_.push_back(handler); } void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } protected: +#ifdef USE_ESP32_BLE_SERVER static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); +#endif +#ifdef USE_ESP32_BLE_CLIENT static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); +#endif static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); bool ble_setup_(); @@ -148,8 +160,12 @@ class ESP32BLE : public Component { // Vectors (12 bytes each on 32-bit, naturally aligned to 4 bytes) std::vector gap_event_handlers_; std::vector gap_scan_event_handlers_; +#ifdef USE_ESP32_BLE_CLIENT std::vector gattc_event_handlers_; +#endif +#ifdef USE_ESP32_BLE_SERVER std::vector gatts_event_handlers_; +#endif std::vector ble_status_event_handlers_; // Large objects (size depends on template parameters, but typically aligned to 4 bytes) From 195d1be4a968f0d365c47ea4b357c21283cac0ba Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:12:39 -0500 Subject: [PATCH 6/9] [version] Reduce flash usage by optimizing string concatenation in setup() (#10890) --- esphome/components/version/version_text_sensor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/version/version_text_sensor.cpp b/esphome/components/version/version_text_sensor.cpp index ed093595cc..65dbfd27cf 100644 --- a/esphome/components/version/version_text_sensor.cpp +++ b/esphome/components/version/version_text_sensor.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/version.h" +#include "esphome/core/helpers.h" namespace esphome { namespace version { @@ -12,7 +13,7 @@ void VersionTextSensor::setup() { if (this->hide_timestamp_) { this->publish_state(ESPHOME_VERSION); } else { - this->publish_state(ESPHOME_VERSION " " + App.get_compilation_time()); + this->publish_state(str_sprintf(ESPHOME_VERSION " %s", App.get_compilation_time().c_str())); } } float VersionTextSensor::get_setup_priority() const { return setup_priority::DATA; } From 875ada86b0553ccdb070581b1fdc4576c856eb18 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:14:16 -0500 Subject: [PATCH 7/9] [web_server] Remove std::find_if overhead matching IDF implementation (#10889) --- esphome/components/web_server/web_server.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 0a97e542c3..03bc17f4fa 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -108,14 +108,14 @@ static UrlMatch match_url(const char *url_ptr, size_t url_len, bool only_domain) void DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_generator_t *message_generator) { DeferredEvent item(source, message_generator); - auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(), - [&item](const DeferredEvent &test) -> bool { return test == item; }); - - if (iter != this->deferred_queue_.end()) { - (*iter) = item; - } else { - this->deferred_queue_.push_back(item); + // Use range-based for loop instead of std::find_if to reduce template instantiation overhead and binary size + for (auto &event : this->deferred_queue_) { + if (event == item) { + event = item; + return; + } } + this->deferred_queue_.push_back(item); } void DeferredUpdateEventSource::process_deferred_queue_() { From b26776fad4b0b245a05d47ddd0ba169957e0932a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:19:06 -0500 Subject: [PATCH 8/9] [select] Remove STL algorithm overhead to reduce flash usage (#10887) --- esphome/components/select/select.cpp | 9 +++++---- esphome/components/select/select_call.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/select/select.cpp b/esphome/components/select/select.cpp index beb72aa320..16e8288ca1 100644 --- a/esphome/components/select/select.cpp +++ b/esphome/components/select/select.cpp @@ -34,11 +34,12 @@ size_t Select::size() const { optional Select::index_of(const std::string &option) const { const auto &options = traits.get_options(); - auto it = std::find(options.begin(), options.end(), option); - if (it == options.end()) { - return {}; + for (size_t i = 0; i < options.size(); i++) { + if (options[i] == option) { + return i; + } } - return std::distance(options.begin(), it); + return {}; } optional Select::active_index() const { diff --git a/esphome/components/select/select_call.cpp b/esphome/components/select/select_call.cpp index a8272f8622..dd398b4052 100644 --- a/esphome/components/select/select_call.cpp +++ b/esphome/components/select/select_call.cpp @@ -107,7 +107,7 @@ void SelectCall::perform() { } } - if (std::find(options.begin(), options.end(), target_value) == options.end()) { + if (!parent->has_option(target_value)) { ESP_LOGW(TAG, "'%s' - Option %s is not a valid option", name, target_value.c_str()); return; } From 1560b8b8e25907a36fb95fdd425a6acb24c90651 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 25 Sep 2025 19:19:48 -0500 Subject: [PATCH 9/9] [climate] Remove STL algorithm overhead in save_state() method (#10888) --- esphome/components/climate/climate.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index be56310b35..e7a454d459 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -367,9 +367,11 @@ void Climate::save_state_() { state.uses_custom_fan_mode = true; const auto &supported = traits.get_supported_custom_fan_modes(); std::vector vec{supported.begin(), supported.end()}; - auto it = std::find(vec.begin(), vec.end(), custom_fan_mode); - if (it != vec.end()) { - state.custom_fan_mode = std::distance(vec.begin(), it); + for (size_t i = 0; i < vec.size(); i++) { + if (vec[i] == custom_fan_mode) { + state.custom_fan_mode = i; + break; + } } } if (traits.get_supports_presets() && preset.has_value()) { @@ -380,10 +382,11 @@ void Climate::save_state_() { state.uses_custom_preset = true; const auto &supported = traits.get_supported_custom_presets(); std::vector vec{supported.begin(), supported.end()}; - auto it = std::find(vec.begin(), vec.end(), custom_preset); - // only set custom preset if value exists, otherwise leave it as is - if (it != vec.cend()) { - state.custom_preset = std::distance(vec.begin(), it); + for (size_t i = 0; i < vec.size(); i++) { + if (vec[i] == custom_preset) { + state.custom_preset = i; + break; + } } } if (traits.get_supports_swing_modes()) {